Macros de Python: ¿Casos de uso?

Si Python tuviera una instalación de macro similar a Lisp / Scheme (algo como MetaPython ), ¿cómo lo usarías ?

Si usted es un progtwigdor de Lisp / Scheme, ¿para qué tipo de cosas usa macros (aparte de las cosas que tienen un claro paralelo sintáctico en Python, como un ciclo while)?

Algunos ejemplos de macros lisp:

  • ITERATE que es una facilidad de bucle divertida y extensible
  • CL-YACC / FUCC que son generadores de analizadores que generan analizadores en tiempo de comstackción
  • CL-WHO que permite especificar documentos html con partes estáticas y dinámicas
  • Parenscript que es un generador de código javascript.
  • Varios envoltorios de código simples, por ejemplo, manejadores de errores (tengo un manejador de mensajes con error-gtk que ejecuta el código y muestra GtkMessageDialog si ocurre un error no manejado), ejecutores (por ejemplo, dado un código, ejecútelo en un hilo diferente; tener una macro dentro del hilo principal que ejecute el código en diferentes subprocesos; la biblioteca PCall usa macros para envolver el código para que se ejecute al mismo tiempo)
  • Constructores de GUI con macros (por ejemplo, especifique la jerarquía de los widgets y las propiedades de los widgets y haga que una macro genere un código para la creación de todos los widgets)
  • Generadores de código que utilizan recursos externos durante el tiempo de comstackción. Por ejemplo, una macro que procesa los encabezados de C y genera un código FFI o una macro que genera definiciones de clases basadas en el esquema de la base de datos
  • Declarativo FFI. Por ejemplo, especificando las estructuras externas, las funciones, sus tipos de argumentos y tener macros para generar las estructuras lisp correspondientes, las funciones con mapeo de tipo y código de cálculo de referencias
  • Los marcos web basados ​​en continuaciones para Common Lisp utilizan macros que transforman el código en forma CPS (estilo de paso de continuación).

Creo que las macros van en contra de la cultura de Python. Las macros en Lisp permiten que se acerque la gran bola de barro ; Tienes la oportunidad de redefinir el idioma para que se adapte mejor a tu dominio de problema. A la inversa, el código Pythonic utiliza la función incorporada más natural de Python para resolver un problema, en lugar de resolverlo de una manera más natural en un idioma diferente.

Las macros son intrínsecamente antipónicas.

Esta es una respuesta un tanto tardía, pero MacroPy es un nuevo proyecto mío para llevar macros a Python. Tenemos una lista bastante sustancial de demostraciones, todas las cuales son casos de uso que requieren macros para implementar, por ejemplo, proporcionan una forma extremadamente concisa de declarar clases:

@case class Point(x, y) p = Point(1, 2) print px # 1 print p # Point(1, 2) 

MacroPy se ha utilizado para implementar características tales como:

  • Clases de casos, tipos de datos algebraicos fáciles de Scala
  • Coincidencia de patrones desde el mundo de la progtwigción funcional
  • Optimización de llamadas de cola
  • Cuasiquotes, una forma rápida de manipular fragmentos de un progtwig.
  • Interpolación de cadenas, una característica común en muchos idiomas, y Pyxl.
  • Seguimiento y afirmaciones inteligentes
  • PINQ a SQLAlchemy, un clon de LINQ a SQL desde C #
  • Lambdas rápidas de Scala y Groovy,
  • Combinadores Parser, inspirados en los de Scala .

Echa un vistazo a la página vinculada para obtener más información; Creo que puedo decir con confianza que los casos de uso que demostramos superan cualquier sugerencia que alguien haya sugerido hasta ahora en este hilo = D

Este es un ejemplo del mundo real que encontré que sería trivial con macros o soporte de metaprogtwigción real, pero se debe hacer con la manipulación del código de bytes de CPython debido a la ausencia de ambos en Python:

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

Así es como se resuelve el problema en Common Lisp usando una combinación de macros regulares y macros de lectura para extender la syntax (se podría haber hecho sin este último, pero no el primero):

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

El mismo problema se resolvió en Smalltalk usando cierres y metaprogtwigción (Smalltalk es uno de los pocos idiomas OO de despacho único que realmente hace que el mensaje se transmita correctamente):

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

Aquí traté de implementar el enfoque de Smalltalk en Common Lisp, que es una buena ilustración de cómo la metaprogtwigción no es compatible en este último:

http://carcaddar.blogspot.com/2009/04/closure-oriented-metaprogramming-via.html

En lisp, las macros son solo otra forma de abstraer ideas.

Este es un ejemplo de un trazador de rayos incompleto escrito en clojure:

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

Si desea hacer algo a cada píxel con coordenadas (i, j), digamos, dibuje un píxel negro si i es par, escribiría:

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

Esto no se puede hacer sin macros porque @body puede significar cualquier cosa dentro (por píxel ij @body)

Algo así sería posible en python también. Necesitas usar decoradores. No puedes hacer todo lo que puedes hacer con macros lisp, pero son muy potentes

Echa un vistazo a este tutorial de decorador: http://www.artima.com/weblogs/viewpost.jsp?thread=240808

Hay una publicación en la lista de correo ( archive.org espejo ) que explica esto bastante bien. La publicación trata sobre Perl, pero también se aplica a Python.

Algunos de los casos de uso que he visto anteriormente incluyen la creación de fábricas de clase o la eliminación de declaraciones de registro fuera del código de producción.

Ver también esta pregunta: Sintaxis de macro pythonica.

No creo que Python necesite macros, porque son útiles para 2 cosas:

  1. Crear un DSL o una syntax más eloquent para algo (la macro Lisp LOOP es un buen ejemplo). En este caso, la filosofía de Python decidió no hacerlo deliberadamente. Si falta alguna notación explícita, siempre puede solicitar un PEP.

  2. Hacer las cosas más rápidas precomputando las cosas en tiempo de comstackción. Python no está orientado a la velocidad, por lo que siempre puedes usar una función.

No estoy diciendo que las macros estén mal, solo que no se ajustan a la filosofía de Python. Siempre puede prescindir de ellos sin mucha duplicación de código, ya que tiene tipeo de pato y sobrecarga de operadores.

Y como nota al margen, preferiría ver los reinicios de Lisp en Python que las macros.

Lee “Los papeles Lambda” para que puedas descubrir en general por qué uno podría aprovechar las macros.

Debes comenzar con ‘AIM-353 Lambda: el imperativo máximo’ y seguirlo con ‘AIM-443 Lambda: el último GOTO’. Ambos se pueden encontrar aquí:

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

Para mi propio uso, creé un módulo Python (Espy) que permite definiciones de macros con argumentos, generación de bucles y códigos condicionales: crea un archivo source.espy, luego inicia la función apropiada y luego se genera source.py.

Permite syntax de la siguiente manera:

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

es equivalente a:

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

Otra syntax:

 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" 

es equivalente a:

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

Más, Espy tiene 2 funciones: “macro para” y “macro si”. Un ejemplo:

 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." 

es traducido por espy en:

 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." 

La documentación completa y la descarga gratuita se pueden encontrar aquí: http://elp.chronocv.fr

Yo uso este módulo en muchos casos. Permite códigos más estructurados y más cortos. Con él, generé 65000 líneas de código Python claro y eficiente a partir de 1000 líneas de código Espy para un nuevo proyecto de motor de ajedrez (todavía en progreso).

Si Python pudiera incluir macros en el lanzamiento futuro, sería más impresionante.

Lo usaría para envolver el yield para poder construir tuberías de generador más potentes.

Actualmente, la única forma en que se pueden agregar funciones a Python es a través de un PEP (Propuesta de mejora de Python). Esto puede ser lento y no le ayuda en los casos en los que desea agregar una función al idioma que solo es útil para su caso de uso.

Por ejemplo, hay un PEP para agregar un bucle do-while . Probablemente, esto se agregará a Python, pero el PEP se creó en 2003. Me gustaría escribir bucles do-while while hoy, y podría hacer eso si Python tuviera macros.

De manera similar, hubo un PEP para agregar un descanso etiquetado y continuar, pero esto fue rechazado. Si las declaraciones rotas rotuladas aclararan mi código, podría implementarlas con una macro.

PEPs aparte, también me gustaría una macro unless . En lugar de escribir:

 if not is_superman(): dodge_bullet() 

Yo podría escribir:

 unless is_superman(): dodge_bullet() 

Me gustaría una macro de case (a menudo llamada cond en Lisp). En lugar de escribir:

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

Yo podría escribir:

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

Estos serían sencillos de implementar como macros. Las macros más complejas y útiles incluirían:

  • Interpolación de cadenas de estilo Ruby, por ejemplo, "hello there {user}" (probablemente mejor implementado como una macro de lector)
  • La coincidencia de patrones

Actualmente, estas son solo características en otros idiomas. Con macros, podría agregarlos a Python. Incluso podría escribir PEPs que incluyeron una implementación de ejemplo. (Algunos PEP ya lo hacen, pero se ven obligados a modificar la fuente C del propio intérprete).

Posiblemente si desea el código fuente en tiempo de ejecución, como para la depuración (por ejemplo, printf depurando el valor de una expresión con el nombre de la misma para que no tenga que escribirlo dos veces).

La única forma en que se me ocurre hacerlo en Python es pasar una cadena a eval.

Bueno, me gustaría en lugar de

 print >> sys.stderr, "abc" 

escribir

 err "abc" 

en algunos scripts que tienen muchas declaraciones de impresión de depuración.

puedo hacer

 import sys err = sys.stderr 

y entonces

 print >> err, "abc" 

que es más corto, pero que todavía tiene demasiados caracteres en la línea.

Quiero usar macro para habilitar sentencias SQL en código python. – seleccione * de la tabla 1

Proveniente de un mundo C, realmente apreciaría poder hacer un registro eficiente de mensajes enriquecidos. En lugar de escribir

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

con macros, uno podría hacer algo como

 DEBUG('%s' % (an_expensive_call_that_gives_useful_info(),))