decoradores de Python y macros Lisp

15

Al mirar a los decoradores de Python, alguien hizo la declaración de que son tan poderosos como las macros Lisp (particularmente Clojure).

Mirando los ejemplos dados en PEP 318 , me parece que son solo una Una forma elegante de usar funciones antiguas de orden superior en Lisp:

def attrs(**kwds):
    def decorate(f):
        for k in kwds:
            setattr(f, k, kwds[k])
        return f
    return decorate

@attrs(versionadded="2.2",
       author="Guido van Rossum")
def mymethod(f):
    ...

No he visto ningún código que se haya transformado en ninguno de los ejemplos, como se describe en Anatomía de una macro de clojure . Además, la homoiconicity faltante de Python podría hacer que las transformaciones de código sean imposibles.

Entonces, ¿cómo se comparan estos dos y puedes decir que son casi iguales en lo que puedes hacer? La evidencia parece apuntar contra ella.

Editar: Basándome en un comentario, busco dos cosas: la comparación en "tan poderoso como" y en "tan fácil hacer cosas increíbles con".

    
pregunta Profpatsch 09.10.2013 - 10:13
fuente

4 respuestas

16

Un decorador es básicamente una function.

Ejemplo en Common Lisp:

(defun attributes (keywords function)
  (loop for (key value) in keywords
        do (setf (get function key) value))
  function)

En la anterior, la función es un símbolo (que sería devuelto por DEFUN ) y colocamos los atributos en la lista de propiedades del símbolo.

Ahora podemos escribirlo alrededor de la definición de una función:

(attributes
  '((version-added "2.2")
    (author "Rainer Joswig"))

  (defun foo (a b)
    (+ a b))

)  

Si queremos agregar una sintaxis elegante como en Python, escribimos una macro de lector . Una macro de lector nos permite programar en el nivel de sintaxis de expresión-s:

(set-macro-character
 #\@
 (lambda (stream char)
   (let ((decorator (read stream))
         (arg       (read stream))
         (form      (read stream)))
     '(,decorator ,arg ,form))))

Entonces podemos escribir:

@attributes'((version-added "2.2")
             (author "Rainer Joswig"))
(defun foo (a b)
  (+ a b))

El lector Lisp lee más arriba que:

(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
                    (AUTHOR "Rainer Joswig")))
            (DEFUN FOO (A B) (+ A B)))

Ahora tenemos una forma de decoradores en Common Lisp.

Combinación de macros y macros de lector.

En realidad haría la traducción anterior en código real usando una macro, no una función.

(defmacro defdecorator (decorator arg form)
  '(progn
     ,form
     (,decorator ,arg ',(second form))))

(set-macro-character
 #\@
 (lambda (stream char)
   (declare (ignore char))
   (let* ((decorator (read stream))
          (arg       (read stream))
          (form      (read stream)))
     '(defdecorator ,decorator ,arg ,form))))

El uso es el anterior con la misma macro de lector. La ventaja es que el compilador Lisp todavía lo ve como un formulario de nivel superior : el * compilador de archivos trata los formularios de nivel superior especialmente, por ejemplo, agrega información sobre ellos al tiempo de compilación entorno . En el ejemplo anterior, podemos ver que la macro mira el código fuente y extrae el nombre.

El lector de Lisp lee el ejemplo anterior en:

(DEFDECORATOR ATTRIBUTES
  (QUOTE ((VERSION-ADDED "2.2")
           (AUTHOR "Rainer Joswig")))
  (DEFUN FOO (A B) (+ A B)))

que luego se macro se expande en:

(PROGN (DEFUN FOO (A B) (+ A B))
       (ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
                           (AUTHOR "Rainer Joswig")))
                   (QUOTE FOO)))

Las macros son muy diferentes de las macros del lector .

Las macros obtienen el código fuente, pueden hacer lo que quieran y luego devolver el código fuente. La fuente de entrada no necesita ser un código Lisp válido. Puede ser cualquier cosa y podría ser escrito totalmente diferente. El resultado tiene que ser un código Lisp válido entonces. Pero si el código generado también usa una macro, entonces la sintaxis del código incrustado en la llamada a la macro podría ser otra sintaxis. Un ejemplo simple: uno podría escribir una macro matemática que aceptaría algún tipo de sintaxis matemática:

(math y = 3 x ^ 2 - 4 x + 3)

La expresión y = 3 x ^ 2 - 4 x + 3 no es un código Lisp válido, pero la macro podría, por ejemplo, analizarlo y devolver un código Lisp válido como este:

(setq y (+ (* 3 (expt x 2))
           (- (* 4 x))
           3))

Hay muchos otros casos de uso de macros en Lisp.

    
respondido por el Rainer Joswig 11.10.2013 - 21:11
fuente
7

En Python (el lenguaje) los decoradores no pueden modificar la función, solo la envuelven, por lo que definitivamente son mucho menos poderosas que las macros lisp.

En CPython (el intérprete), los decoradores pueden modificar la función porque tienen acceso al bytecode, pero la función se compila primero y puede ser modificada por el decorador, por lo que no es posible modificar la sintaxis, una cosa lisp-macro-equivalente tendría que hacer.

Tenga en cuenta que las lisps modernas no utilizan las expresiones S como código de bytes, por lo que las macros que trabajan en las listas de expresiones S definitivamente funcionan antes de la compilación del código de bytes como se indicó anteriormente, en python el decorador corre detrás de él.

    
respondido por el Jan Hudec 09.10.2013 - 10:42
fuente
4

Es bastante difícil utilizar decoradores de Python para introducir nuevos mecanismos de flujo de control.

Es confuso en trivial utilizar macros de Common Lisp para introducir nuevos mecanismos de flujo de control.

De esto, probablemente se deduce que no son tan expresivos (elijo interpretar "poderoso" como "expresivo", ya que creo que lo que realmente significan).

    
respondido por el Vatine 09.10.2013 - 13:32
fuente
0

Ciertamente es una funcionalidad relacionada, pero desde un decorador de Python no es trivial modificar el método al que se llama (ese sería el parámetro f en tu ejemplo). Para modificarlo, podría volverse loco con el módulo ast ), pero Sería para una programación bastante complicada.

Se han hecho cosas en esta línea: revisa el paquete macropy para ver algunos ejemplos realmente alucinantes.

    
respondido por el Jacob Oscarson 09.10.2013 - 10:37
fuente

Lea otras preguntas en las etiquetas