Реклама
Комментарии 8
А зачем все это?
Сделаем из питона еще одного монстра типа C++?
Тот же pytest простые вещи делает сложным и неочевидным путем.
С помощью макросов можно сложные вещи делать более простыми в использовании.

Так же можно оптимизировать код под конкретную зада, собирая функции из AST по кусочкам.

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

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

А есть какие-нить способы посмотреть преобразованный код? Без загрузки модуля
Например для отладки макроса
Для этого надо запустить процесс кодогенерации и напечатать получившийся результат. Например в REPL.

Есть такой Python пакет – «meta». В нём есть утилиты для работы с AST, в том числе и для печати AST дерева. Можно использовать его.

В этом туториале можно почитать подробнее: macropy3.readthedocs.io/en/latest/ast.html

А в Common Lisp, к примеру, раскрытие макросов встроено в язык и в IDE. Его можно вот так вызвать из REPL:

;; Сначала определим макрос:
CL-USER> (defmacro trace-forms ((&optional (stream t))
                        &body body)
           `(progn ,@(loop for form in body
                           collect `(format ,stream "~S -> ~S~%"
                                            ',form
                                            ,form))))
TRACE-FORMS

;; Вот так он работает:
CL-USER> (trace-forms ()
           1
           :foo
           "bar"
           (+ 1 3))
1 -> 1
:FOO -> :FOO
"bar" -> "bar"
(+ 1 3) -> 4
NIL

;; А так можно посмотреть,  в какой код он раскрывается:
CL-USER> (macroexpand-1
          '(trace-forms ()
            1
            :foo
            "bar"
            (+ 1 3)))
(PROGN
 (FORMAT T "~S -> ~S~%" '1 1)
 (FORMAT T "~S -> ~S~%" ':FOO :FOO)
 (FORMAT T "~S -> ~S~%" '"bar" "bar")
 (FORMAT T "~S -> ~S~%" '(+ 1 3) (+ 1 3)))
T
Метапрограммирование выглядят очень круто, но на деле сложно в сопровождении. Собственно, почему метаклассы и не рекомендуется писать налево и направо. Возможно, где-то и есть мифический баланс между крутостью и читаемостью, но я его пока не встречал. А отладка такого кода потребует пары часов *непрерывной* концентрации, что не всегда возможно.
Только полноправные пользователи могут оставлять комментарии. , пожалуйста.