Програмування метаклассом в python

Огляд об'єктно-орієнтованого програмування

Давайте почнемо з короткого огляду того, що являє собою ООП. В об'єктно-орієнтованих мовах програмування ви можете визначити класи. які з'єднують пов'язані між собою дані і моделі поведінки. Класи можуть наслідувати деякі або всі характеристики від батьківських класів, але в них можуть також визначатися власні атрибути (дані) і методи (моделі поведінки). В кінцевому рахунку класи зазвичай служать шаблонами для створення екземплярів (іноді також званих просто об'єктами). Як правило, в різних телефонах одного класу містяться різні дані, але всі вони знаходяться в одній формі - наприклад, у обох об'єктів співробітників Employee bob і jane є заробітна плата .salary і кімната .room_number. але величина заробітної плати і номер кімнати у них різняться.

Інші матеріали цієї серії

Деякі мови ООП, в тому числі Python, передбачають створення інтроспективних об'єктів (також називаються рефлексивними). Інтроспективні об'єкти - це об'єкти, які можуть описувати самі себе: До якого класу належить даний екземпляр? Які батьківські елементи є у цього класу? Які методи і атрибути доступні даному об'єкту? Інтроспекція дозволяє функцій або методів, що обробляють об'єкт, приймати рішення на основі того, який вид об'єкта обробляється. Навіть без інтроспекції функції часто розгалужуються на основі даних примірника - наприклад, шлях до jane.room_number відрізняється від шляху до bob.room_number. оскільки вони знаходяться в різних кімнатах. За допомогою інтроспекції ви можете безпечно розрахувати розмір премії для jane. не виконуючи при цьому розрахунок для bob. наприклад, тому, що у jane є атрибут .profit_share. або тому, що bob є екземпляром підкласу Hourly (Employee).

відповідь метапрограмування

Базова система ООП, коротко описана вище, вельми потужна. Однак в наведеному описі проглядається ще один момент: класи в Python (і в інших мовах) самі є об'єктами, які можуть бути передані і інтроспектіровани. Оскільки об'єкти, як було зазначено вище, створюються за допомогою класів, використовуваних як шаблони, то за якими шаблонами створюються класи? Відповідь очевидна - це метакласи.

Метакласи в Python були завжди. Однак механізм реалізації метаклассом починаючи з версії Python 2.2 став набагато доступнішим. Зокрема, починаючи з версії 2.2, Python перестав бути мовою тільки з одним спеціальним (здебільшого прихованим) метаклассом, на підставі якого створюється будь-який об'єкт класу. Тепер програміст може створювати підкласи кореневого метаклассом type і навіть динамічно формувати класи з використанням різних метаклассом. Звичайно ж, той факт, що ви можете маніпулювати метаклассом в Python 2.2, сам по собі не пояснює, навіщо вам це може бути потрібно.

Більш того, для створення власних класів вам не потрібні власні метакласи. Трохи більш зрозумілою концепцією є фабрика класів. Звичайна функція може повертати клас, який був створений динамічно в тілі функції. У традиційному синтаксисі Python ви можете написати:

Функція фабрики class_with_method () динамічно створює і повертає клас, який містить методи і функції, передані фабриці. Перед тим, як клас буде повернутий, функція виробляє з ним деякі операції. У модулі new представлено більш короткий написання, але без можливості власного коду в тілі фабрики класів, наприклад:

У всіх цих випадках (Foo. Foo2) поведінка класу не прописується безпосередньо в коді, натомість воно створюється викликом функції в реальному часі, з динамічними аргументами. Потрібно підкреслити, що ми динамічно створюємо не екземпляри класів. а самі класи.

Метакласи: рішення, яке шукає проблему?

Метакласи - це дуже глибока матерія, про яку 99% користувачів навіть не потрібно замислюватися. Якщо ви не розумієте, навіщо вони вам потрібні - значить, вони вам не потрібні (люди, яким вони насправді потрібні, точно знають, що вони їм потрібні, і їм не потрібно пояснювати - чому). - Тім Пітерс (Tim Peters), гуру по Python

Методи (класів), як і звичайні функції, можуть повертати об'єкти. Тому в певному сенсі очевидно, що фабрики класів можуть так само просто бути класами, як вони можуть бути функціями. Зокрема, в Python 2.2 + реалізований спеціальний клас type. який є саме такою фабрикою класів. Звичайно ж, читачі згадають менш амбітне функцію type (). вбудовану в старі версії Python - на щастя, поведінка старої функції type () збережено класом type (іншими словами, type (obj) повертає тип / клас об'єкта obj). Новий клас type працює як фабрика класів, точно так же, як функція new.classobj.

Однак, оскільки тепер type - це (мета) клас, ми можемо вільно створити його підклас:

Магічні методи .__ new __ () і .__ init __ () носять особливий характер, але концептуально вони такі ж, як і у будь-якого іншого класу. Метод .__ init __ () дозволяє налаштувати створений об'єкт; метод .__ new __ () дозволяє налаштувати місце його розміщення. Останнє, звичайно, використовується не так часто, але можливо для будь-якого класу, виконаного в новому стилі Python 2.2 (як правило, успадковується, але не заміщується).

У нащадків type є одна особливість, яка потребує особливої ​​уваги; на ній спотикаються всі, хто перший раз працює з метаклассом. Перший аргумент цих методів зазвичай називається cls. а не self. тому що методи працюють з створеним класом, а не з метаклассом. Насправді тут немає нічого особливого; всі методи зв'язуються зі своїми екземплярами, а екземпляром метаклассом є клас. Ситуацію трохи прояснює неспециальное ім'я:

Вся ця напрочуд пересічна механіка супроводжується деякими синтаксичними зручностями, які, з одного боку, полегшують роботу з метаклассом, а з іншого - збивають з пантелику нових користувачів. Цей додатковий синтаксис включає кілька елементів. Однак порядок вирішення цих змін дуже заплутаний. Класи можуть успадковувати метакласи від своїх батьків - зверніть увагу, що це не те ж саме, що мати метакласи як батьків (ще одна поширена помилка). Для класів старого стилю завдання глобальної змінної _metaclass_ могло примусити використовувати власний метаклассом. Однак в більшості випадків, і це найбільш безпечний підхід, для класу, який необхідно створити за допомогою власного метаклассом, задають атрибут класу _metaclass_. Цю змінну необхідно задати безпосередньо в визначенні класу, оскільки метаклассом не використовується, якщо атрибут буде встановлено пізніше (після того, як об'єкт класу вже буде створений). наприклад:

Рішення проблем за допомогою магії

Отже, ми розглянули основні ідеї метаклассом. Однак використання метаклассом в реальній роботі - досить тонка справа. Проблема використання метаклассом полягає в тому, що в звичайній архітектурі ООП класи по суті роблять зовсім небагато. Структура успадкування класів корисна для інкапсуляції і упаковки даних і методів, але користувач, як правило, працює тільки з конкретними екземплярами.

Як і можна було б припустити, це додаток виводить досить загальний опис об'єкта data (звичайний об'єкт примірника). Однак якщо цього додатка в реальному часі передати аргументи, ми можемо отримати зовсім інший результат:

У цьому прикладі використовується сериализация gnosis.xml.pickle. але в більшості сучасних пакетів gnosis.magic також містяться серіалізатор метаклассом MetaYamlDump. MetaPyPickler і MetaPrettyPrint. Крім того, користувач »у програмі« dump.py може зажадати використання будь-якого "MetaPickler" з будь-якого пакета Python, де це передбачено. Текст метаклассом, що відповідає цій меті, буде виглядати наступним чином:

Одне з чудових досягнень такого механізму полягає в тому, що програмісту, що пише додаток, не обов'язково знати, яка сериализация буде використовуватися, так само як і про те, чи буде додана сериализация або будь-яка інша перехресна технологія в командному рядку.

Мабуть, найпоширеніше застосування метаклассом схоже з використанням MetaPicklers: додавання, видалення і заміщення методів, визначених у створеному класі. У нашому прикладі "оригінальний" спосіб Data.dump () замінюється іншим, створеним поза додатки, в момент створення класу Data (і, отже, в кожному наступному екземплярі).

Інші способи вирішення проблем за допомогою магії

Якщо ви спробуєте створити екземпляр класу dissertation без необхідних вкладених елементів компонентів, буде повернута описова помилка; те ж саме вірно і для кожного вкладеного елемента. Правильні вкладені елементи будуть створені з простіших аргументів, коли є тільки один спосіб недвозначного «підняття» аргументів до правильного типу.

Навіть незважаючи на те, що класи перевірки часто (неформально) грунтуються на вже існуючих DTD, екземпляри цих класів виводять самі себе як неоформлені фрагменти документа XML, наприклад:

Пакет gnosis.xml.validity нічого не знає про DTD і внутрішніх подмножествах. В цілому ці концепції і можливості представлені метаклассом DTDGenerator. без будь-яких змін в gnosis.xml.validity і simple_diss.py. DTDGenerator не підставляти власний метод .__ str __ () в створювані їм класи - ви все ще можете роздрукувати неоформлений фрагмент XML - але цей метаклассом дозволяє легко змінювати такі магічні методи.

Інструменти для метаклассом

У пакеті gnosis.magic міститься кілька утиліт для роботи з метаклассом, а також кілька прикладів метаклассом, які можна використовувати в аспект-орієнтованому програмуванні. Найбільш важливою з цих утиліт є import_with_metaclass (). Ця функція, використана в наведеному вище прикладі, дозволяє вам імпортувати модуль стороннього виробника, але створити всі класи цього модуля з використанням власного метаклассом, а не type. Ви можете визначити в створюваному вами (або взагалі взятому звідкись ще) метаклассом будь-яку нову можливість, яку ви хочете ввести в модуль стороннього виробника. gnosis.magic містить кілька підключаються метаклассом сериализации; в інших пакетах є засоби трасування, збереження об'єктів, протоколювання помилок і т.п.

Функція import_with_metclass () ілюструє ряд переваг програмування з використанням метаклассом:

Одна з особливостей цієї функції полягає в тому, що звичайний клас Meta створюється за допомогою вказаного метаклассом. Однак після додавання Meta як батько його нащадки також створюються за допомогою спеціального метаклассом. По суті, класи типу Meta можуть нести в собі і метаклассом-творець, і набір успадкованих методів - ці дві сторони його спадщини незалежні один від одного.

Ресурси для скачування

Схожі теми