Макросы Python: варианты использования?

Если бы в Python была функция макросов, аналогичная Lisp/Scheme (что-то вроде MetaPython), как бы вы используете его?

Если вы программист Lisp/Scheme, для чего вы используете макросы (кроме вещей, которые имеют четкую синтаксическую параллель в Python, таких как цикл while)?


person Rick Copeland    schedule 18.04.2009    source источник
comment
Вы знаете о проекте Logix? livelogix.com/logix   -  person codeape    schedule 20.04.2009
comment
Я смотрел на Logix давным-давно, но не недавно. Это интересный подход. Спасибо за указатель!   -  person Rick Copeland    schedule 20.04.2009
comment
@codeape: ссылка не работает   -  person Casebash    schedule 16.05.2010
comment
Да, к сожалению и к сожалению, я бы, наверное, использовал их (когда мне это не нужно).   -  person Paul Draper    schedule 16.04.2013
comment
Ссылка на проект Logix: logix-language.sourceforge.net   -  person Chris Medrela    schedule 09.12.2013


Ответы (16)


Некоторые примеры лисп-макросов:

  • ITERATE — забавная и расширяемая функция циклов.
  • CL-YACC/FUCC, которые являются генераторами синтаксических анализаторов, которые генерируют синтаксические анализаторы во время компиляции.
  • CL-WHO, который позволяет указывать HTML-документы со статической и динамической частями.
  • Parenscript, который является генератором кода javascript.
  • Различные простые обёртки кода, например, обработчики ошибок (у меня есть with-gtk-error-message-handler, который выполняет код и показывает GtkMessageDialog, если возникает необработанная ошибка), исполнители (например, получив код, выполняйте его в другом потоке; я иметь макрос внутри основного потока, который выполняет код в разных потоках; библиотека PCall использует макросы для переноса кода выполняться одновременно)
  • Построители графического интерфейса с макросами (например, укажите иерархию виджетов и свойства виджетов и генерируйте код макроса для создания всех виджетов)
  • Генераторы кода, использующие внешние ресурсы во время компиляции. Например, макрос, обрабатывающий заголовки C и генерирующий код FFI, или макрос, генерирующий определения классов на основе схемы базы данных.
  • Декларативное ИФУ. Например, указание сторонних структур, функций, типов их аргументов и наличие макросов для генерации соответствующих структур Лиспа, функций с отображением типов и кодом маршалинга.
  • Веб-фреймворки на основе продолжений для Common Lisp используют макросы, которые преобразуют код в форму CPS (стиль передачи продолжения).
person dmitry_vk    schedule 19.04.2009
comment
-1: Примеры из lisp не очень хорошо применимы к Python. Если это можно сделать в Python без макросов, то на самом деле это не варианты использования Python, не так ли? - person S.Lott; 20.04.2009
comment
Единственный способ сделать большинство из них в Python — это расширить синтаксис. Но это всегда верно: если вы заставляете сопровождающего языка расширять язык для каждой функции, которую вы можете захотеть, это, по сути, то, что делают макросы. Только с помощью макросов вам не нужно каждый раз разветвлять язык или спрашивать Гвидо. - person Ken; 20.04.2009
comment
С.Лотт: Вопрос не в применимости вариантов использования, а в реальных вариантах использования. Строго говоря, когда у вас есть eval(), вы можете делать все без макросов. Я думаю, что такой аргумент контрпродуктивен. Я не думаю, что каждый из упомянутых примеров можно выполнить на Python без потери важных функций (например, минимальные/небольшие накладные расходы во время выполнения, обнаружение ошибок во время компиляции, интеграция с остальной частью языка). - person dmitry_vk; 21.04.2009
comment
Вы можете делать все с eval() только в том случае, если вы хотите поместить весь свой код в строки и написать свой собственный синтаксический анализатор/переводчик. Но если это правда, вам даже не нужен eval(): достаточно любого языка, эквивалентного Тьюрингу. - person Ken; 21.04.2009
comment
Кен: можно иметь eval без парсера. Некоторые lisp (например, newlisp) имеют F-Exprs. Fexpr — это своего рода функция, которая принимает невычисленные аргументы как выражения (поддеревья AST) и может решать, что с ними делать: eval, изменить и eval, отбросить и т. д. Конечно, с fexprs есть огромные проблемы. - person dmitry_vk; 25.04.2009
comment
Другой случай, который здесь рассматривается, — это ранняя/нетерпеливая оценка во время компиляции. Как dmitry-vk упоминает в своем ответе, библиотеку синтаксического анализатора, которая генерирует конечные автоматы синтаксического анализатора (очень дорогостоящие) во время компиляции байт-кода. python не компилируется в традиционном смысле, но преобразует исходный код в байт-код, а затем кэширует его. Было бы очень полезно кэшировать эти дорогие конечные автоматы. - person Aaron; 29.04.2009

Я считаю, что макросы противоречат культуре Python. Макросы в Лиспе допускают подход большой ком грязи; вы можете переопределить язык, чтобы он больше подходил для вашей предметной области. И наоборот, код Pythonic использует наиболее естественную встроенную функцию Python для решения проблемы, вместо того, чтобы решать ее таким образом, который был бы более естественным на другом языке.

Макросы по своей сути не являются питоническими.

person RossFabricant    schedule 18.04.2009
comment
Это своего рода напрашивается вопрос, не так ли? Если бы в язык были добавлены макросы, по этой логике они стали бы питоническими, так же как декораторы, списки, условные выражения, метаклассы и т. д. не считались бы питоническими до того, как они были добавлены в качестве встроенных функций в язык программирования. язык. Что делает макросы менее питоническими, чем, скажем, использование sys._getframe() и метаклассов? - person Rick Copeland; 19.04.2009
comment
Вы были бы правы, но обычно это ссылка на PEP 20 python.org/ dev/peps/pep-0020 и тому подобные, которые описывают общую философию языка. В этом случае я думаю, что макросы, вероятно, будут противоречить нескольким связанным рекомендациям. - person Dana the Sane; 19.04.2009
comment
Я знаком с дзен Python; Я просто не уверен, что макросы обязательно противоречат этому. - person Rick Copeland; 19.04.2009
comment
Я думаю, это будет зависеть от того, как они будут реализованы. Глядя на ссылку метапитона, указанную в вопросе, я вижу несколько проблем. - person Dana the Sane; 19.04.2009
comment
Стиль Python подобен стилю шрифта. Понимание списка и др. похожи на разные буквы в шрифте Python. Макросы похожи на мета-правила, которые позволяют вам создавать собственный шрифт. - person RossFabricant; 20.04.2009
comment
Макросы не нужны, что делает их непитоновскими. Разумных вариантов использования нет. Примеры из других языков здесь не применимы. Макросы — это обходной путь на уровне исходного кода для языковых пробелов. - person S.Lott; 20.04.2009
comment
Макросы — это точка в пространстве компромиссов в языковом дизайне. Они находят компромисс между производительностью и динамизмом (ограничивая необходимость вызова eval во время выполнения, позволяя компилятору применять логику для оптимизации, проверки типов и вывода типов). Макросы необходимы, чтобы иметь возможность вводить полезные абстракции, сохраняя при этом возможность выполнять полезные оптимизации. Это одна из причин, почему реализации Common Lisp быстрые, а CPython медленные — нельзя легко устранить накладные расходы интерпретатора. - person dmitry_vk; 21.04.2009
comment
@dmitry Я думаю, что компромисс между мощными абстракциями и ясностью кода. Производительность кажется не связанной. - person RossFabricant; 21.04.2009
comment
rossfabricant: взгляните на en.wikipedia.org/wiki/Fexpr . Fexprs обеспечивают такую ​​же ясность кода и являются более мощными, чем макросы (с точки зрения вычислений, которые возможны с ними; все, что можно сделать с макросами, можно сделать с fexprs, но не наоборот). - person dmitry_vk; 25.04.2009
comment
Суть макросов заключается в изобретении новых (лучших?) способов сказать одно и то же. Делать одно и то же, только по-разному, наверное, звучит очень не по-питоновски. Проконсультируйтесь с импортом этого. - person Aaron; 29.04.2009

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

@case
class Point(x, y)

p = Point(1, 2)
print p.x   # 1
print p     # Point(1, 2)

MacroPy использовался для реализации таких функций, как:

  • Классы case, простые алгебраические типы данных из Scala
  • Сопоставление с образцом из мира функционального программирования
  • Оптимизация хвостового вызова
  • Квазикавычки, быстрый способ манипулировать фрагментами программы
  • Интерполяция строк, общая функция многих языков, и Pyxl.
  • Трассировка и смарт-утверждения
  • PINQ to SQLAlchemy, клон LINQ to SQL из C#
  • Быстрые лямбда-выражения из Scala и Groovy,
  • Комбинаторы парсеров, вдохновленные Scala.

Посетите связанную страницу, чтобы узнать больше; Я думаю, что могу с уверенностью сказать, что варианты использования, которые мы демонстрируем, намного превосходят все, что кто-либо предлагал до сих пор в этой теме = D

person Li Haoyi    schedule 11.05.2013
comment
+1: вы определенно понимаете, почему макросы должны существовать намного лучше, чем кто-либо другой, кто ответил. Сказав это, я не люблю вашу библиотеку. Использование созданных им макросов кажется более сложным, чем необходимо, а написание макросов с его помощью намного, намного сложнее, чем необходимо. - person ArtOfWarfare; 31.07.2014

Существует публикация в списке рассылки (зеркалоarchive.org), что довольно хорошо объясняет это. Этот пост посвящен Perl, но он также применим и к Python.

person lfaraone    schedule 18.04.2009

В lisp макросы — это просто еще один способ абстрагировать идеи.

Это пример из незавершенного трассировщика лучей, написанного на clojure:

(defmacro per-pixel
  "Macro.
Excecutes body for every pixel. Binds i and j to the current pixel coord."
  [i j & body]
  `(dotimes [~i @width]
     (dotimes [~j @height]
       ~@body)))

Если вы хотите что-то сделать с каждым пикселем с координатами (i,j), скажем, нарисовать черный пиксель, если i четно, вы должны написать:

(per-pixel i,j
  (if (even? i)
    (draw-black i,j)))

Без макросов здесь не обойтись, потому что @body может означать что угодно внутри (попиксельно i j @body)

Что-то подобное было бы возможно и в питоне. Вам нужно использовать декораторы. Вы не можете делать все, что можете, с помощью макросов lisp, но они очень мощные.

Ознакомьтесь с этим руководством по декоратору: http://www.artima.com/weblogs/viewpost.jsp?thread=240808

person bigmonachus    schedule 18.04.2009
comment
в Lisp: напишите функцию PER-PIXEL и передайте тело как другую функцию. Макрос не нужен. - person Rainer Joswig; 19.04.2009
comment
Да, я тоже не понимаю, почему это хороший пример макроса. - person Hannes Ovrén; 19.04.2009
comment
Вам не нужны макросы для этого в Лиспе. Вам также не нужны декораторы для этого в Python. Это просто многомерная функция типа foreach. Почему не per_pixel(обратный вызов), где per_pixel вызывает обратный вызов с j и i. В Лиспе обратный вызов может быть лямбдой. В Python это тоже возможно (но с точки зрения Python лямбда-выражения ограничены одиночными выражениями). - person ; 05.03.2012

Вот один реальный пример, с которым я столкнулся, который был бы тривиален с макросами или реальной поддержкой метапрограммирования, но должен быть выполнен с манипуляцией байт-кодом CPython из-за отсутствия обоих в Python:

http://www.aminus.net/dejavu/chrome/common/doc/2.0a/html/intro.html#cpython

Вот как проблема решается в Common Lisp с помощью комбинации обычных макросов и read-макросов для расширения синтаксиса (можно было бы обойтись без последних, но не с первыми):

http://clsql.b9.com/manual/csql-find.html

Та же проблема решена в Smalltalk с использованием замыканий и метапрограммирования (Smalltalk — один из немногих ОО-языков с единой диспетчеризацией, который действительно обеспечивает правильную передачу сообщений):

http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg02096.html

Здесь я попытался реализовать подход Smalltalk в Common Lisp, что является хорошей иллюстрацией того, как плохо поддерживается метапрограммирование в последнем:

http://carcaddar.blogspot.com/2009/04/закрытие-ориентированное-метапрограммирование-via.html

person vsedach    schedule 14.03.2010

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

person Rick Copeland    schedule 18.04.2009

Я не думаю, что Python нуждается в макросах, потому что они полезны для двух вещей:

  1. Создание DSL или более красноречивого синтаксиса для чего-либо (хороший пример — макрос Lisp LOOP). В данном случае философия Python преднамеренно отказалась от него. Если вам не хватает какой-то явной записи, вы всегда можете запросить PEP.

  2. Делайте вещи быстрее, предварительно вычисляя вещи во время компиляции. Python не ориентирован на скорость, поэтому вместо этого вы всегда можете использовать функцию.

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

И в качестве примечания, я бы предпочел перезапуски Lisp в Python, чем макросы.

person Community    schedule 19.04.2009
comment
Часть домена в предметно-ориентированном языке не означает новый, более глупый способ написания циклов for. domaindrivendesign.org - person vsedach; 15.03.2010
comment
Итак, встроенная функция макросов Python — это PEP? :-) - person Domingo Ignacio; 28.08.2010

Прочтите "The Lambda Papers", чтобы понять, почему вообще можно воспользоваться макросами.

Вы должны начать с «AIM-353 Lambda: The Ultimate Imperative» и следовать за ним с «AIM-443 Lambda: Ultimate GOTO». Оба можно найти здесь:

http://library.readscheme.org/page1.html

person grettke    schedule 11.06.2009

Hy, Для собственного использования я создал модуль Python (Espy), который позволяет определять макросы с аргументами, циклами и генерацией условного кода: вы создаете файл source.espy, затем запускаете соответствующую функцию, затем генерируется source.py.

Он допускает следующий синтаксис:

macro repeat(arg1):
    for i in range(%arg1%):
        socket
    print "stop"
   ...
repeat(5):
    print "Hi everybody"
    print "See you soon"

эквивалентно:

...
for i in range(5):
    print "Hi everybody"
    print "See you soon"
print "stop"

Другой синтаксис:

macro doit(arg1):
    for i in %arg1%:
        socket suit(arg2):
            socket
            print %arg2%
        socket check(arg3):
            if %arg2%==%arg3%:
                socket
...
#use
doit(range(10)):
    suit(result):
        result=i*i
    check(result,25):
        print "I knew that 5*5 == 25"

эквивалентно:

for i in range(10):
    result=i*i
    print result
    if result==25:
        print "I knew that 5*5 == 25"

Еще у Espy есть 2 функции: "макрос для" и "макрос если". Пример:

macro for v in [6,10,12,20,23]:
    macro if 7<%v%<22:
        True:
            print "At %v%, I'm awake."
        False:
            print "At %v%, I'm sleeping."

переводится Эспи на:

print "At 6, I'm sleeping."
print "At 10, I'm awake."
print "At 12, I'm awake."
print "At 20, I'm awake."
print "At 23, I'm sleeping."

Полную документацию и бесплатную загрузку можно найти здесь: http://elp.chronocv.fr

Я использую этот модуль во многих случаях. Это позволяет использовать более структурированные и короткие коды. С его помощью я сгенерировал 65000 строк четкого и эффективного кода Python из 1000 строк шпионского кода для нового проекта шахматного движка (все еще в процессе).

Если бы Python мог включать макросы в будущую версию, это стало бы более впечатляющим.

person elp    schedule 01.06.2011

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

person pi.    schedule 25.03.2013

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

Например, есть PEP для добавления цикла do-while. Это, вероятно, будет добавлено в Python, но PEP был создан в 2003 году. Я хотел бы написать do-while циклов сегодня, и я мог бы сделать это, если бы в Python были макросы.

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

Помимо PEP, мне также нужен макрос unless. Вместо того, чтобы писать:

if not is_superman():
    dodge_bullet()

Я мог бы написать:

unless is_superman():
    dodge_bullet()

Мне нужен макрос case (часто называемый cond в Лиспе). Вместо того, чтобы писать:

if x == FOO:
    do_stuff_with_foos()
elif x == BAR:
    do_stuff_with_bars()
elif x == BAZ:
    do_stuff_with_bazs()

Я мог бы написать:

switch x:
   case FOO:
       do_stuff_with_foos()
   case BAR:
       do_stuff_with_bars()
   case BAZ:
       do_stuff_with_bazs()

Их было бы просто реализовать в виде макросов. Более сложные и полезные макросы включают:

  • Интерполяция строк в стиле Ruby, например. "hello there {user}" (вероятно, лучше всего реализовано в виде макроса для чтения)
  • Сопоставление с образцом

В настоящее время это только функции на других языках. С помощью макросов я мог добавить их в Python. Я мог бы даже написать PEP, который включал бы пример реализации. (Некоторые PEP уже делают это, но они вынуждены модифицировать исходный код C самого интерпретатора.)

person Wilfred Hughes    schedule 23.04.2013
comment
Хорошие простые примеры. Я бы добавил в ваш список выражение let и функцию ifelse. - person yardsale8; 02.02.2017

Возможно, если вам нужен исходный код во время выполнения, например, для отладки (скажем, printf отлаживает значение выражения с его именем, поэтому вам не нужно писать его дважды).

Единственный способ можно было бы придумать, чтобы сделать это в python, чтобы передать строку в eval.

person Roman A. Taycher    schedule 15.01.2012

Ну, я хотел бы вместо

print >> sys.stderr, "abc"

написать

err "abc"

в некоторых сценариях, которые имеют много операторов распечатки отладки.

я могу сделать

import sys
err = sys.stderr

а потом

print >> err, "abc"

который короче, но все равно занимает слишком много символов в строке.

person Evgeni Sergeev    schedule 10.04.2012
comment
Если вы просто определяете функцию, вы можете написать err("abc"), что, безусловно, лучше, чем print >> err, "abc", и не намного хуже, чем err "abc" - person abarnert; 28.03.2013
comment
Хорошая мысль: def err(*args): print >> sys.stderr, ' '.join(map(str, args)). - person Evgeni Sergeev; 02.04.2013

Я хочу использовать макрос, чтобы включить оператор sql в коде python. - выбрать * из таблицы1

person alex    schedule 24.01.2013

Приходя из C-мира, я был бы очень признателен за возможность эффективно регистрировать расширенные сообщения. Вместо того, чтобы писать

if logger.level > logger.DEBUG:
    logger.log('%s' % (an_expensive_call_that_gives_useful_info(),))

с макросами вместо этого можно было бы сделать что-то вроде

DEBUG('%s' % (an_expensive_call_that_gives_useful_info(),))
person Mr Fooz    schedule 11.03.2014