Разное

Метапрограммирование c: Метапрограммирование в C++ / Хабр

Содержание

через страдания к просветлению / Хабр

#define variant_TL1( T1 ) detail::typelist< T1, detail::nulltype >
#define variant_TL2( T1, T2) detail::typelist< T1, variant_TL1( T2) >
#define variant_TL3( T1, T2, T3) detail::typelist< T1, variant_TL2( T2, T3) >
#define variant_TL4( T1, T2, T3, T4) detail::typelist< T1, variant_TL3( T2, T3, T4) >
#define variant_TL5( T1, T2, T3, T4, T5) detail::typelist< T1, variant_TL4( T2, T3, T4, T5) >
#define variant_TL6( T1, T2, T3, T4, T5, T6) detail::typelist< T1, variant_TL5( T2, T3, T4, T5, T6) >
#define variant_TL7( T1, T2, T3, T4, T5, T6, T7) detail::typelist< T1, variant_TL6( T2, T3, T4, T5, T6, T7) >
#define variant_TL8( T1, T2, T3, T4, T5, T6, T7, T8) detail::typelist< T1, variant_TL7( T2, T3, T4, T5, T6, T7, T8) >
#define variant_TL9( T1, T2, T3, T4, T5, T6, T7, T8, T9) detail::typelist< T1, variant_TL8( T2, T3, T4, T5, T6, T7, T8, T9) >
#define variant_TL10( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) detail::typelist< T1, variant_TL9( T2, T3, T4, T5, T6, T7, T8, T9, T10) >
#define variant_TL11( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) detail::typelist< T1, variant_TL10( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) >
#define variant_TL12( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) detail::typelist< T1, variant_TL11( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) >
#define variant_TL13( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) detail::typelist< T1, variant_TL12( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) >
#define variant_TL14( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) detail::typelist< T1, variant_TL13( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) >
#define variant_TL15( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) detail::typelist< T1, variant_TL14( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) >
#define variant_TL16( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) detail::typelist< T1, variant_TL15( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) >

Что такое метапрограммирование?

метапрограммирование относится к различным способам, которыми программа обладает знаниями о себе или может манипулировать собой.

в таких языках, как C#, отражение является формой метапрограммирования, поскольку программа может изучать информацию о себе. Например, возврат списка всех свойств объекта.

в таких языках, как ActionScript, вы можете оценивать функции во время выполнения для создания новых программ, таких как eval(«x» + i).DoSomething () повлияет на объект с именем x1, когда i равно 1 и x2, когда i равно 2.

наконец, еще одна распространенная форма метапрограммирования — это когда программа может меняться нетривиальным образом. LISP хорошо известен этим и является тем, что Пол Грэм отстаивал около десяти лет назад. Мне придется просмотреть некоторые из его эссе. Но идея в том, что программа изменит другую часть программы на основе ее состояния. Это позволяет уровень гибкости принимать решения во время выполнения, что очень сложно в большинстве популярных языки сегодня.

также стоит отметить, что в старые добрые времена программирования в прямой сборке программы, которые изменили себя во время выполнения, были необходимы и очень распространены.

из эссе Пола Грэма «Что Сделало Lisp Другим»:

многие языки имеют то, что называется
макрос. Но макросы Lisp уникальны. И
хотите верьте, хотите нет, но они
связано с круглыми скобками. Этот
дизайнеры Lisp не забыть
круглые скобки в языке просто
отличающийся. К программисту Blub,
Код Lisp выглядит странно. Но эти
скобки там не просто так.
Они являются внешним свидетельством
принципиальная разница между Лиспом
и на других языках.

код Lisp сделан из данных Lisp
объекты. И не в тривиальном смысле
что исходные файлы содержат
символы и строки являются одним из
типы данных, поддерживаемые языком.
Код Лиспа, после его читает
анализатор, состоит из структур данных
что ты можешь пройти.

Если вы понимаете, как работают составители,
на самом деле происходит не так уж много.
что Лисп имеет странный синтаксис, что
Лиспа нет синтаксиса. Вы пишете программы
в деревьях разбора, которые генерируются
внутри компилятора, когда другое
анализируются языки. Но эти разбор
деревья полностью доступны для вашего
программы. Вы можете написать программы, которые
манипулировать ими. В Lisp, эти
программы называется макрос. Они
программы, которые пишут программы.

программы, которые пишут программы? Когда
ты бы когда-нибудь захотел это сделать? Не
очень часто, если вы думаете в Cobol. Все
время, если думать шепеляво. Он
было бы удобно если бы я мог
приведите пример мощного макроса,
и скажи там! как насчет этого? Но если
Я сделал, это будет выглядеть как
тарабарщина для того, кто не знает
Шепелявить; здесь нет места объяснять.
все, что нужно знать, чтобы
понимаю, что это значит. В Ansi
Common Lisp я пытался переместить вещи
так быстро, как только мог, и даже так.
Я не макросы, пока страница 160.

но я думаю, что могу дать своего рода
аргумент, который может быть убедительным. Этот
исходный код редактора Viaweb был
вероятно, около 20-25% макросы. Макросы
труднее писать, чем обычный Lisp
функции, и считается, что
плохой стиль использовать их, когда они не
необходимый. Так что каждый макрос в этот код
есть, потому что это должно быть. Что?
это означает, что по крайней мере 20-25%
код в этой программе делает
вещи, которые ты не можешь легко сделать в любом
другой язык. Однако скептически
Программист реветь, может быть, о моем
претензии на таинственные силы
Шепелявит, это должно вызвать у него любопытство.
Мы не писали этот код для наших
собственное развлечение. Мы были крошечным стартапом.,
программируем так усердно, как только можем.
порядок возведения технических барьеров
между нами и нашими конкуренты.

подозрительный человек может начать
интересно, была ли какая-то корреляция?
здесь. Большой кусок нашего кода
делать вещи, которые очень трудно сделать
на других языках. В результате
программное обеспечение сделало то, что наши конкуренты
программное обеспечение не могло этого сделать. Может быть, так оно и было.
какая-то связь. Я поощряю
вы должны следовать этой нити. Могут
более что старик ковыляет себе
на костылях, чем кажется на первый взгляд.

Метапрограммирование — Википедия

Материал из Википедии — свободной энциклопедии

Метапрограммирование — вид программирования, связанный с созданием программ, которые порождают другие программы как результат своей работы[1] (в частности, на стадии компиляции их исходного кода), либо программ, которые меняют себя во время выполнения (самомодифицирующийся код).[источник не указан 3387 дней] Первое позволяет получать программы при меньших затратах времени и усилий на кодирование, чем если бы программист писал их вручную целиком, второе позволяет улучшить свойства кода (размер и быстродействие).

Генерация кода

При этом подходе код программы не пишется вручную, а создаётся автоматически программой-генератором на основе другой программы.

Такой подход приобретает смысл, если при программировании вырабатываются различные дополнительные правила (более высокоуровневые парадигмы, выполнение требований внешних библиотек, стереотипные методы реализации определённых функций и пр.). При этом часть кода (или данных) теряет содержательный смысл и становится лишь механическим выполнением правил. Когда эта часть становится значительной, возникает мысль задавать вручную лишь содержательную часть, а остальное добавлять автоматически. Это и проделывает генератор.

Различаются два принципиально различных вида кодогенерации:

  1. генератор является физически отдельной бинарной программой, необязательно написанной на целевом языке.
  2. целевой язык является одновременно языком реализации генератора, так что метапрограмма составляет с целевой программой единое целое.

Наиболее распространённым и наглядным примером первого случая являются GUI построители, где метапрограмма нацелена на пользовательское программирование[en], позволяя несведущим в программировании специалистам в области эргономики принимать непосредственное участие в разработке программных продуктов. В этом случае метапрограмма оказывается заведомо намного более сложной, крупной и трудоёмкой в разработке, чем порождаемый ею код, и её разработка оправдывается частотой её использования. Следует отметить, что на практике, как правило[источник не указан 1010 дней] (но не обязательно), такого рода метапрограммы пишутся на императивных языках для использования в императивных же языках, и поставляются в скомпилированном виде. Недостатком этого метода является невозможность повторного использования кода метапрограммы при разработке новых, более сложных метапрограмм.

Другим примером являются генераторы синтаксических и лексических анализаторов, такие как Lex, YACC, ANTLR, bison.

Второй случай представляет собой встраивание языка и реализуется тремя статическими методами c использованием макросредств языка или чистым встраиванием. В этом случае наработки, накопленные в процессе разработки метапрограмм, в дальнейшем могут интенсивно повторно использоваться для разработки новых метапрограмм[2].

Другие примеры:

  • В Perl существует понятие «source filters» («фильтров исходного кода») — метода переработки файлов с исходным кодом перед выполнением, позволяющего полностью менять синтаксис и семантику языка. Одним из известных примеров является модуль Lingua::Romana::Perligata, позволяющий писать код Perl на латыни.[3]
  • В Форт программисту предоставляют встроенные в язык возможности по изменению своего синтаксиса и семантики. Это достигается определением архитектуры виртуальной машины и полным доступом к возможностям изменения её составляющих.

Возможность изменять или дополнять себя во время выполнения превращает программу в виртуальную машину. Хотя такая возможность существовала уже давно на уровне машинных кодов (и активно использовалась, например, при создании полиморфных вирусов), с метапрограммированием обычно связывают перенос подобных технологий в высокоуровневые языки.

Основные методы реализации:

  • Гомоиконность — инструкции по самоидентификации или изменению кода основываются на том же синтаксисе, что и сам код.
  • Интроспекция — представление внутренних структур языка в виде переменных встроенных типов с возможностью доступа к ним из программы (в терминах С++ это будет соответствовать технологиям динамического полиморфизма и динамического приведения типов).
Позволяет во время выполнения просматривать, создавать и изменять определения типов, стек вызовов, обращаться к переменной по имени, получаемому динамически и пр.

  • Пространство имён System.Reflection и тип System.Type в .NET; классы Class, Method, Field в Java; представление пространств имен и определений типов через встроенные типы данных в Python; стандартные встроенные возможности в Форт по доступу к ресурсам виртуальной машины; получение значения и изменение свойств почти любого из объектов в ECMAScript (с оговорками).
  • Интерпретация произвольного кода, представленного в виде строки.
    • Существует естественным образом во множестве интерпретируемых языков (впервые функция eval была реализована в Lisp, а точнее, непосредственно перед ним, ставшим её первым реализованным интерпретатором).
    • Компилятор Tiny C позволяет «на лету» компилировать и исполнять код на языке C, представленный в виде строки символов.
    • Для Форт использования процедуры интерпретации из строки EVALUATE.

В языке Пролог метапрограммирование позволяет автоматизировать разработку и верификацию (проверку свойств) Пролог-программ. Метапрограммы рассматривают программы на Прологе как термы и позволяют анализировать их свойства и взаимоотношения, строить программы для управления другими Пролог-программами[4].

См. также

Примечания

Ссылки

Десять причин избегать метапрограммирования | Записки программиста

В любой команде рано или поздно появляется человек, который совсем недавно прочитал книжку по Lisp или осилил Template Haskell и потому ему не терпится применить метапрограммирование на практике. Однако проблема заключается в том, что в большинстве случаев макросы или шаблоны создают больше проблем, чем решают. В этой заметке будет показано, почему так.

Примечание: Далее под макросами и шаблонами я буду иметь в виду средства метапрограммирования, не привязанные к конкретному языку. Это могут быть макросы в Clojure или Scala, шаблоны в Haskell, parse_transform и, опять таки, макросы в Erlang, шаблоны в C++ и так далее. Все эти средства примерно об одном и том же.

  1. Вообще, трудно представить или даже специально придумать задачу, которую невозможно решить без макросов. Мне в голову приходило использовать макросы 1-2 раза, и то задачу можно было решить иначе. Почти всегда проблему на самом деле можно решить обычными средствами языка.
  2. На практике использование макросов всегда приводит к существенному увеличению времени компиляции проекта. Также существенно увеличиваются требования к ресурсам, необходимым для компиляции, в особенности оперативной памяти;
  3. Никто не отменял обычную кодогенерацию. Например, Thrift, Protobuf и прочие делают в сущности то же самое, что вы хотите от макросов. Если же такие решения плохо интегрируются с вашей системой сборки или IDE, возможно, просто у вас фиговая система сборки или IDE.
  4. Допустим, код, полученный с помощью шаблонов или макросов, бросит исключение. Какие номера строк вы увидите в стэктрейсе? Сможете ли вы легко разобраться в проблеме и исправить ее, особенно, если проблемный код написан кем-то другим?
  5. Если вы используете шаблоны, то скорее всего сломаете мозг своей любимой IDE. Допустим, шаблон генерирует новые функции. Чтобы узнать о них, IDE нужно иметь встроенный интерпретатор вашего языка программирования. Скорее всего, она его не имеет, а значит будет подчеркивать сгенерированные функции красным, когда вы будете пытаться их использовать, так как в коде их как бы и нет.
  6. Возможно, вы пытаетесь замаскировать при помощи шаблонов дублирование кода. Есть менее радикальные способы борьбы с code smell. Дублированный код почти всегда можно вынести в отдельные методы. Или параметризовать различающиеся части, передав лямбду.
  7. Большинство программистов просто не умеют работать с макросами и шаблонами. Помните, что вы не одни в команде. Даже если ваши коллеги знают макросы, все равно их использование существенно усложнит понимание и поддержку кода.
  8. Зачастую макросы могут быть причиной странных и непонятных ошибок. Например, в Erlang вы можете собрать beam с какими-то макросами, затем обновить зависимость, в которой объявлен этот макрос, и собрать остальную часть проекта уже с другим макросом. Попробуйте потом отладить ошибки, которые посыпятся! Или, допустим, макрос использует текущее время. Вы один раз скомпилировали код, он закэшировался. Затем вы пересобираете проект и не понимаете, почему время не изменяется.
  9. Нередко макросы — это нестабильная и вообще экспериментальная фича языка. В Scala и так все постоянно меняется, а вы еще предлагаете взять макросы. В Template Haskell тоже вон все поменялось, теперь там есть какие-то типизированные макросы. Плюс к этому в реализации макросов нередко имеются какие-то мелкие косяки и неудобства. Так в Template Haskell шаблон не может быть объявлен и использован в одном файле.
  10. Вы изобретаете кучу различных DSL. Каждый язык при этом, понятное дело, отличается от других. Почему бы просто не писать на одном языке? Может быть, он просто недостаточно выразителен и следует найти язык получше?

Как видите, минусов у метапрограммирования масса, и, сказать по правде, я не вижу существенных плюсов.

Пожалуйста, поймите меня правильно. Не то, чтобы макросы абсолютно бесполезны и их никогда не следует использовать. В редких случаях они могут быть довольно полезны. В этом смысле они напоминают наследование, имплиситы в Scala или даже строгую статическую типизацию. Это все средства, без которых, вообще говоря, в большинстве случаев можно обойтись и потому они только приносят лишнюю сложность. Фокус в том, что в некоторых случаях они очень, очень удобны, так удобны, что их включают в язык. Scala, например, как бы «сложный», но очень «удобный» язык. А вот Scheme — язык «простой», но «неудобный». Был бы еще проще и неудобнее, не будь в нем макросов.

А используете ли вы метапрограммирование в своей работе и если да, то в каких задачах?

Метки: Разработка, Философия.

Метапрограммирование — Википедия

Материал из Википедии — свободной энциклопедии

Метапрограммирование — вид программирования, связанный с созданием программ, которые порождают другие программы как результат своей работы[1] (в частности, на стадии компиляции их исходного кода), либо программ, которые меняют себя во время выполнения (самомодифицирующийся код).[источник не указан 3387 дней] Первое позволяет получать программы при меньших затратах времени и усилий на кодирование, чем если бы программист писал их вручную целиком, второе позволяет улучшить свойства кода (размер и быстродействие).

Генерация кода

При этом подходе код программы не пишется вручную, а создаётся автоматически программой-генератором на основе другой программы.

Такой подход приобретает смысл, если при программировании вырабатываются различные дополнительные правила (более высокоуровневые парадигмы, выполнение требований внешних библиотек, стереотипные методы реализации определённых функций и пр.). При этом часть кода (или данных) теряет содержательный смысл и становится лишь механическим выполнением правил. Когда эта часть становится значительной, возникает мысль задавать вручную лишь содержательную часть, а остальное добавлять автоматически. Это и проделывает генератор.

Различаются два принципиально различных вида кодогенерации:

  1. генератор является физически отдельной бинарной программой, необязательно написанной на целевом языке.
  2. целевой язык является одновременно языком реализации генератора, так что метапрограмма составляет с целевой программой единое целое.

Наиболее распространённым и наглядным примером первого случая являются GUI построители, где метапрограмма нацелена на пользовательское программирование[en], позволяя несведущим в программировании специалистам в области эргономики принимать непосредственное участие в разработке программных продуктов. В этом случае метапрограмма оказывается заведомо намного более сложной, крупной и трудоёмкой в разработке, чем порождаемый ею код, и её разработка оправдывается частотой её использования. Следует отметить, что на практике, как правило[источник не указан 1010 дней] (но не обязательно), такого рода метапрограммы пишутся на императивных языках для использования в императивных же языках, и поставляются в скомпилированном виде. Недостатком этого метода является невозможность повторного использования кода метапрограммы при разработке новых, более сложных метапрограмм.

Другим примером являются генераторы синтаксических и лексических анализаторов, такие как Lex, YACC, ANTLR, bison.

Второй случай представляет собой встраивание языка и реализуется тремя статическими методами c использованием макросредств языка или чистым встраиванием. В этом случае наработки, накопленные в процессе разработки метапрограмм, в дальнейшем могут интенсивно повторно использоваться для разработки новых метапрограмм[2].

Другие примеры:

  • В Perl существует понятие «source filters» («фильтров исходного кода») — метода переработки файлов с исходным кодом перед выполнением, позволяющего полностью менять синтаксис и семантику языка. Одним из известных примеров является модуль Lingua::Romana::Perligata, позволяющий писать код Perl на латыни.[3]
  • В Форт программисту предоставляют встроенные в язык возможности по изменению своего синтаксиса и семантики. Это достигается определением архитектуры виртуальной машины и полным доступом к возможностям изменения её составляющих.

Возможность изменять или дополнять себя во время выполнения превращает программу в виртуальную машину. Хотя такая возможность существовала уже давно на уровне машинных кодов (и активно использовалась, например, при создании полиморфных вирусов), с метапрограммированием обычно связывают перенос подобных технологий в высокоуровневые языки.

Основные методы реализации:

  • Гомоиконность — инструкции по самоидентификации или изменению кода основываются на том же синтаксисе, что и сам код.
  • Интроспекция — представление внутренних структур языка в виде переменных встроенных типов с возможностью доступа к ним из программы (в терминах С++ это будет соответствовать технологиям динамического полиморфизма и динамического приведения типов).
Позволяет во время выполнения просматривать, создавать и изменять определения типов, стек вызовов, обращаться к переменной по имени, получаемому динамически и пр.

  • Пространство имён System.Reflection и тип System.Type в .NET; классы Class, Method, Field в Java; представление пространств имен и определений типов через встроенные типы данных в Python; стандартные встроенные возможности в Форт по доступу к ресурсам виртуальной машины; получение значения и изменение свойств почти любого из объектов в ECMAScript (с оговорками).
  • Интерпретация произвольного кода, представленного в виде строки.
    • Существует естественным образом во множестве интерпретируемых языков (впервые функция eval была реализована в Lisp, а точнее, непосредственно перед ним, ставшим её первым реализованным интерпретатором).
    • Компилятор Tiny C позволяет «на лету» компилировать и исполнять код на языке C, представленный в виде строки символов.
    • Для Форт использования процедуры интерпретации из строки EVALUATE.

В языке Пролог метапрограммирование позволяет автоматизировать разработку и верификацию (проверку свойств) Пролог-программ. Метапрограммы рассматривают программы на Прологе как термы и позволяют анализировать их свойства и взаимоотношения, строить программы для управления другими Пролог-программами[4].

См. также

Примечания

Ссылки

Метаклассы и метапрограммирование в Python — GitJournal

4.5
/
5
(
2

голоса
)

Представьте себе, что у вас есть компьютерные программы, которые пишут код за вас. Это возможно, но машины не напишут весь ваш код!

Эта методика, именуемая метапрограммированием, популярна среди разработчиков фреймворков. Так вы получаете генерацию кода и умные возможности во многих распространённых фреймворках и библиотеках, таких как Ruby On Rails или TensorFlow.

Языки фукционального программирования, такие как Elixir, Clojure и Ruby, известны своими возможностями метапрограммирования. В этом руководстве мы покажем, как вы можете использовать мощь метапрограммирования в Python.

Примеры кода написаны для Python 3, но будут работать на Python 2 с некоторыми исправлениями.

Что такое метакласс в Python?

Python — это объектно-оринтированный язык, который облегчает работу с классами.

Метапрограммирование в Python основано на новом особом типе классов, которые называются метаклассами. Этот тип класса, вкратце, содержит инструкции о закулисной генерации кода, который вы хотите получить, запуская другой код.

Википедия довольно хорошо описывает метаклассы:

Метакласс (англ. Metaclass) — в объектно-ориентированном программировании это класс, экземпляры которого в свою очередь являются классами.

Когда мы определяем класс, его объекты создаются, используя класс как пример. Но как насчёт самого класса? Что есть этот самый пример для класса?

Здесь на сцену выходит метакласс. Метакласс — это пример самого класса, как класс есть пример для его экземпляров. Метакласс — это класс, который определяет свойства других классов. С помощью метакласса мы можем определять свойства, которые следует добавить к новым классам, определяемымв нашем коде.

Например, метакласс в следующем образце кода добавляет свойство hello к каждому классу, использующему этот метакласс как шаблон. Это значит, что новые классы — экземпляры этого метакласса будут иметь свойство hello без необходимости определять его отдельно:

# hello_metaclass.py
# Простой метакласс
# Этот метакласс добавляет метод 'hello' к классам, использующим его значение,
# те классы получают метод 'hello' без лишних усилий
# метакласс заботится о генерации кода для нас
class HelloMeta(type):  
    # Метод hello
    def hello(cls):
        print("greetings from %s, a HelloMeta type class" % (type(cls())))

    # Вызываем метакласс
    def __call__(self, *args, **kwargs):
        # создаём новый класс как обычно
        cls = type.__call__(self, *args)

        # определяем новый метод hello для каждого из этих классов
        setattr(cls, "hello", self.hello)

        # возвращаем класс
        return cls

# Проверяем метакласс
class TryHello(object, metaclass=HelloMeta):  
    def greet(self):
        self.hello()

# Создаём экземпляр метакласса. Он должен автоматически содержать метод hello
# хотя он не объявлен в классе вручную
# иными словами, он объявлен за нас метаклассом
greeter = TryHello()  
greeter.greet()

В результате запуска этого кода новый класс TryHello способен напечатать приветствие:

greetings from <class '__main__.TryHello'>, a HelloMeta type class

Метод, ответственный за этот вывод, не объявлен в декларации класса. Вместо этого метакласс, в данном случае HelloMeta, порождает код во время запуска, что сразу связывает этот метод с классом.

Чтобы увидеть это в действии, смело копируйте код в консоль Python. Также прочтите комментарии, чтобы лучше понимать, что мы сделали в каждой части кода. У нас есть новый объект по имени greeter, который является сущностью класса TryHello. Впрочем, мы можем вызвать метод self.hello класса TryHello, хотя этот метод не определён в объявлении класса TryHello.

Вместо того, чтобы выдать ошибку вызова несуществующего метода, TryHello автоматически привязывает этот метод к классу, используя HelloMeta как его метакласс.

Метаклассы дают нам возможность писать код, который изменяет не только данные, но и другой код, то есть изменяет класс во время его создания. В примере выше наш метакласс автоматически добавляет новый метод к новым классам, которые мы определяем, чтобы использовать метакласс.

Это пример метапрограммирования. Метапрограммирование — это просто написание кода, который работает с метаклассами и схожие методики изменения кода на заднем плане.

Прекрасно в метапрограммировании то, что вместо вывода исходного кода, оно даёт нам лишь исполнение этого кода. Пользователь нашей программы не знает о «магии», происходящей за кадром.

Подумайте о фреймворках, которые порождают код в фоновом режиме, чтобы убедиться, что вам как программисту нужно писать меньше кода для всего. Вот несколько очень хороших примеров:

Помимо языка Python, другие библиотеки вроде Ruby On Rails(Ruby) и Boost(C++) служат примерами того, как метапрограммирование используется авторами фреймворков, чтобы неявно порождать код и заботиться обо всем. В результате получаем упрощённые пользовательские API, которые автоматизируют много работы за программиста, пишущего код фреймворка.

Обеспечить, чтобы простота работала за кадром — как раз то, для чего метапрограммирование встроено в исходный код фреймворков.

Немного теории: разберёмся, как работают метаклассы

Чтобы понять, как работают метаклассы в Python, Вам нужно очень хорошо понимать нотацию типов. Тип — это просто номенклатура данных или объекта в Python.

Найти тип объекта

Используя Python REPL (командный интерпретатор), давайте создадим простой строковый объект и проверим его тип:

>>> day = "Sunday"
>>> print("The type of variable day is %s" % (type(day)))
The type of variable day is <type 'str'>  

Как и следовало ожидать, мы получили вывод, что переменная day относится к типу str, строковому типу. Вы можете найти тип любого объекта, просто используя встроенную функцию type с одним аргументом-объектом.

Найти тип класса

Так, строка вроде "Sunday" или "hello" имеет тип str, но как насчёт самого str ? Какой тип у класса str ?

Опять введём в консоль Python:

>>> type(str)
<type 'type'>  

В этот раз мы получаем вывод: str принадлежит типу type.

Тип и тип его типа

Что скажем про сам type? Каков тип type?

>>> type(type)
<type 'type'>  

В результате снова получаем «type». Так мы находим, что type есть не только метакласс для типов наподобие int, но и свой собственный метакласс!

Специальные методы, которые используются метаклассами

Здесь нам поможет немного теории. Вспомним, что метакласс — это класс, сущностями которого являются сами классы, а не просто обычные объекты. В Python 3 можно назначить метакласс при создании нового класса, передав главный класс в определение нового класса.

Тип type, как метакласс по умолчанию в Python, определяет особые методы, которые новый метакласс может переписать, чтобы создать поведение уникального кода. Вот краткий обзор этих «волшебных» методов, существующих в метаклассе:

  • __new__: этот метод вызывается в метаклассе до того, как создаётся сущность класса, на нём основанного
  • __init__: этот метод вызывается, чтобы установить переменные после создания сущности/объекта
  • __prepare__: определяет пространство имён класса в отображении, сохраняющием атрибуты
  • __call__: этот метод вызывается, когда конструктор нового класса нужно использовать для создания оъекта

Вот методы, переопределение которых в вашем метаклассе даст вашим классам поведение, отличное от типа typeметакласса по умолчанию.

Практика метапрограммирования 1: использование декораторов, чтобы изменить поведение функции

Сделаем шаг назад, прежде чем мы приступим к практике метапрограммирования. Обычное применение метапрограммирования на Python — это использоваине декораторов.

Декоратор есть функция, которая изменяет исполнение функции. Иными словами, он принимает функцию на вход и возвращает другую функцию.

Например, вот декоратор, который берёт функцию и печатает её имя перед обычным запуском функции. Это может быть полезно для логирования вызовов функции, например:

# decorators.py

from functools import wraps

# Создаём новый декоратор по имени notifyfunc
def notifyfunc(fn):  
    """печатает имя функции перед её исполнением"""
    @wraps(fn)
    def composite(*args, **kwargs):
        print("Executing '%s'" % fn.__name__)
        # Запускаем исходную функцию и возвращаем результат
        rt = fn(*args, **kwargs)
        return rt
    # Возвращаем нашу сложную функцию
    return composite

# Применяем наш декоратор к обычной функции, которая печатает произведение своих аргументов
@notifyfunc
def multiply(a, b):  
    product = a * b
    return product

Вы можете скопировать и вставить этот код в Python REPL. Главное в использовании декораторов — то, что исполняется составная функция вместо входной. В результате исполнения следующего кода функция умножения объявляет о своём запуске перед началом вычислений:

>>> multiply(5, 6)
Executing 'multiply'  
30  
>>>
>>> multiply(89, 5)
Executing 'multiply'  
445  

Вкратце, декораторы достигают того же изменяющего код поведения метаклассов, но они гораздо проще. Было бы лучше использовать декораторы, когда нужно применить общее метапрограммирование вокруг своего кода. Например, можно написать декоратор, логирующий все вызовы базы данных.

Практика метапрограммирования 2: использование метаклассов как функций-декораторов

Метаклассы могут заменять или изменять атрибуты или классы. Они способны запускаться перед созданиием нового объекта, или после того как он уже создан. В итоге получаем большую гибкость относительно того, для чего их можно применить.

Далее мы создадим метакласс, достигающий того же результата, что и декоратор из прошлого примера. Чтобы сравнить оба способа, вам следует запустить их совместно. Заметьте, что можно скопировать код и вставить его прямо в ваш REPL, если он поддерживает форматирование кода.

# metaclassdecorator.py
import types

# Функция, которая возвращает имя поступающей на вход функции и возвращает новую функцию
# инкапсулирующию поведение исходной функции
def notify(fn, *args, **kwargs):

    def fncomposite(*args, **kwargs):
        # Обычная функциональность notify
        print("running %s" % fn.__name__)
        rt = fn(*args, **kwargs)
        return rt
    # Возвращаем сложную функцию
    return fncomposite

# Метакласс, меняющий поведение своих классов
# на новые методы, 'дополненные' поведением преобразователя сложной функции
class Notifies(type):

    def __new__(cls, name, bases, attr):
        # Заменим каждую функцию на выражение, которое печатает имя функции
        # перед запуском вычисления с предоставленными args и возвращает его результат
        for name, value in attr.items():
            if type(value) is types.FunctionType or type(value) is types.MethodType:
                attr[name] = notify(value)

        return super(Notifies, cls).__new__(cls, name, bases, attr)

# Проверим метакласс
class Math(metaclass=Notifies):  
    def multiply(a, b):
        product = a * b
        print(product)
        return product

Math.multiply(5, 6)

# Запуск multiply():
# 30


class Shouter(metaclass=Notifies):  
    def intro(self):
        print("I shout!")

s = Shouter()  
s.intro()

# Запуск intro():
# I shout!

В классах, которые используют наш метакласс Notifies, например Shouter и Math, во время создания заменены методы на дополненные версии, которые сначала сообщают нам через выражение print имя запускаемого сейчас метода. Это идентично поведению, которое мы имплементировали перед использованием функции-декоратора.

Пример метаклассов 1: объявление класса, от которого нельзя унаследоваться

Обычные сценарии использования для метапрограммирования включают контроль сущностей классов. Например, одиночки (singletons) используются во многих библиотеках кода. Класс-одиночка контролирует создание сущностей так, что может существовать лишь один экземпляр класса в программе.

Финальный класс — другой пример того, как можно контролировать использование классов. Этот класс не позволяет создавать свои подклассы. Финальные классы используются в некоторых фреймворках для безопасности, чтобы убедиться, что класс сохраняет свои исходные атрибуты.

Ниже мы дадим объявление финального класса, использующего метакласс, который не позволяют классу наследоваться от другого.

# final.py

# финальный метакласс. Унаследоваться от класса, имеющего свои метаклассом Final, не получится
class Final(type):  
    def __new__(cls, name, bases, attr):
        # от финального класса нельзя унаследоваться
        # проверим, что класс Final не выступает в качестве базового
        # если так, укажем об ошибке, иначе создадим новый класс с атрибутами Final
        type_arr = [type(x) for x in bases]
        for i in type_arr:
            if i is Final:
                raise RuntimeError("You cannot subclass a Final class")
        return super(Final, cls).__new__(cls, name, bases, attr)


# Тест: применим метакласс, чтобы создать финальный класс Cop

class Cop(metaclass=Final):  
    def exit():
        print("Exiting...")
        quit()

# Попытка создать класс Cop, в идеале следует возбудить исключение!
class FakeCop(Cop):  
    def scam():
        print("This is a hold up!")

cop1 = Cop()  
fakecop1 = FakeCop()

# Больше тестов, другой класс Final
class Goat(metaclass=Final):  
    location = "Goatland"

# Унаследоваться от финального класса не получится
class BillyGoat(Goat):  
    location = "Billyland"

В этом коде мы ввели объявления классов, чтобы попробовать унаследоваться от класса Final. Эти попытки провалились, в результате было выброшено исключение. Использование метаклассов, что ограничивают наследование, позволяет нам объявить финальные классы в нашей кодовой базе.

Пример метаклассов 2: создание класса, отслеживающего время исполнения

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

Мы можем использовать метакласс, чтобы следить за временем исполнения кода. Наш пример кода не совсем профилировщик, но он показывает, как можно метапрограммированием решать подобные задачи.

# timermetaclass.py	 	 
import types	 	 
# Класс функций времени	 	 
import time	 	 
class Timer: 	 	 
 def __init__(self, func=time.perf_counter):	 	 
 self.elapsed = 0.0	 	 
 self._func = func	 	 
 self._start = None	 	 
 def start(self):	 	 
 if self._start is not None:	 	 
 raise RuntimeError('Already started')	 	 
 self._start = self._func()	 	 
 def stop(self):	 	 
 if self._start is None:	 	 
 raise RuntimeError('Not started')	 	 
 end = self._func()	 	 
 self.elapsed += end - self._start	 	 
 self._start = None	 	 
 def reset(self):	 	 
 self.elapsed = 0.0	 	 
 @property	 	 
 def running(self):	 	 
 return self._start is not None	 	 
 def __enter__(self):	 	 
 self.start()	 	 
 return self	 	 
 def __exit__(self, *args):	 	 
 self.stop()	 	 
# Далее мы создаём метакласс Timed, который считает время работы своих методов	 	 
# вместе с функциями установки, которые переписывают методы классa	 	 
# времена создания классов	 	 
# Функция, засекающая время исполнения встроенной функции, возвращает новую,	 	 
# инкапсулируя поведение исходной функции	 	 
def timefunc(fn, *args, **kwargs):	 	 
 def fncomposite(*args, **kwargs):	 	 
 timer = Timer()	 	 
 timer.start()	 	 
 rt = fn(*args, **kwargs)	 	 
 timer.stop()	 	 
 print("Executing %s took %s seconds." % (fn.__name__, timer.elapsed))	 	 
 return rt	 	 
 # возвращает сложную функцию	 	 
 return fncomposite

# Метакласс 'Timed', который заменяет методы своих классов	
# с новым методами 'timed' на поведение сложной функции-преобразователя	 	 
class Timed(type):	 	 
 def __new__(cls, name, bases, attr):	 	 
 # меняет каждую функцию на новую, время которой замеряется	 	 
 # запускает вычисление с заданными args и возвращает результат	 	 
 for name, value in attr.items():	 	 
 if type(value) is types.FunctionType or type(value) is types.MethodType:	 	 
 attr[name] = timefunc(value)	 	 
 return super(Timed, cls).__new__(cls, name, bases, attr)	 	 
# Следующий пример кода проверяет метакласс	 	 
# Классы, применяющие метакласс Timed, следует замерить за нас автоматически	 	 
# проверьте результат в REPL	 	 
class Math(metaclass=Timed):	 	 
 def multiply(a, b):	 	 
 product = a * b	 	 
 print(product)	 	 
 return product	 	 
Math.multiply(5, 6)	 	 
class Shouter(metaclass=Timed):	 	 
 def intro(self):	 	 
 print("I shout!")	 	 
s = Shouter() 	 	 
s.intro()	 	 
def divide(a, b): 	 	 
 result = a / b	 	 
 print(result)	 	 
 return result	 	 
div = timefunc(divide) 	 	 
div(9, 3)

Выводы

Как можно видеть, мы смогли создать метакласс Timed, котоый переписывает сови классы на лету. Когда бы ни объявлялся новый класс, использующийTimed, его методы переписываются, чтобы наш класс замерял время его работы. Когда бы мы ни запускали вычисления с помощью класса Timed , замеры делаются за нас сами, без нужды делать что-либо ещё.

Метапрограммирование — важная вещь, если вы пишете код и инструменты для применения другими разработчиками, в частности фреймворков или отладчиков. С генерацией кода и метапрограммированием вы можете облегчить жизнь программистов, которые пользуются вашими библиотеками кода.

Возможно ли метапрограммирование в C #?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Выразительное метапрограммирование шаблонов C ++ — Свободно C ++

Есть часть разработчиков C ++, которые ценят метапрограммирование шаблонов.

И есть все остальные разработчики C ++.

Хотя я считаю, что скорее попадаю в лагерь поклонников, я встречал гораздо больше людей, которые не проявляли к нему особого интереса или даже находили его совершенно отвратительным , чем энтузиастов TMP.В какой лагерь вы попадаете?

На мой взгляд, одна из причин, по которой TMP не нравится многим людям, заключается в том, что часто это непонятный . До такой степени, что иногда это выглядит как темная магия, предназначенная для очень своеобразного подвида разработчиков, которые могут понять ее диалект. Конечно, время от времени мы сталкиваемся с понятным фрагментом TMP, но в среднем мне труднее понять, чем обычный код.

И я хочу сказать, что TMP не обязательно должно быть таким .

Я собираюсь показать вам, как сделать код TMP более выразительным. И это не ракетостроение.

TMP часто называют языком в рамках языка C ++. Итак, чтобы сделать TMP более выразительным, нам просто нужно применить те же правила, что и в обычном коде. Чтобы проиллюстрировать это, мы возьмем фрагмент кода, который могут понять только самые смелые из нас, и применим к нему следующие два правила выразительности:

Я сказал вам, это не ракетостроение.

Перед тем, как мы начнем, я хочу поблагодарить своего коллегу Джереми за его впечатляющую ловкость с TMP, а также Винсента, с которым всегда так здорово находить отклик в идеях.Ребята, вы молодцы.

Назначение кода

Мы напишем API, который проверяет, действительно ли выражение для данного типа.

Например, учитывая тип T, мы хотели бы знать, является ли T увеличиваемым, то есть для объекта t типа T, независимо от того, является ли выражение:

является действительным. Если T равно int , тогда выражение допустимо, а если T равно std :: string , тогда выражение недействительно.

Вот типичная часть TMP, которая его реализует:

шаблон
struct is_incrementable: std :: false_type {};

шаблон
struct is_incrementable ())>
>: std :: true_type {};

.

Метапрограммирование на C ++ и D

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

.

c ++ 14 — Практическое метапрограммирование на C ++

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Добавить комментарий

Ваш адрес email не будет опубликован.