Decoratori Python e macro Lisp

15

Quando guardi i decoratori Python qualcuno ha fatto la dichiarazione, che sono potenti come le macro Lisp (in particolare Clojure).

Osservando gli esempi forniti in PEP 318 mi sembra che siano solo un modo elegante di usare semplici vecchie funzioni di ordine superiore in 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):
    ...

Non ho visto alcun codice che si trasforma in nessuno degli esempi, come descritto in Anatomia di una macro Clojure . Inoltre, la mancanza di di omoiconicity potrebbe rendere le trasformazioni del codice impossibili.

Quindi, come si confrontano questi due e puoi dire che sono circa uguali in quello che puoi fare? L'evidenza sembra puntare contro di essa.

Modifica: in base a un commento, sto cercando due cose: confronto su "tanto potente quanto" e su "cose facili da fare fantastiche con".

    
posta Profpatsch 09.10.2013 - 10:13
fonte

4 risposte

16

Un decoratore è fondamentalmente solo una funzione .

Esempio in Common Lisp:

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

In sopra la funzione è un simbolo (che verrebbe restituito da DEFUN ) e inseriamo gli attributi nella lista di proprietà del simbolo.

Ora possiamo scrivere attorno a una definizione di funzione:

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

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

)  

Se vogliamo aggiungere una sintassi di fantasia come in Python, scriviamo una macro del lettore . Una macro reader ci permette di programmare sul livello della sintassi dell'espressione s:

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

Quindi possiamo scrivere:

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

Il Lisp reader legge sopra a:

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

Ora abbiamo una forma di decoratori in Common Lisp.

Combinazione di macro e macro di lettori.

Effettivamente farei la traduzione sopra nel codice reale usando una macro, non una funzione.

(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))))

L'uso è come sopra con la stessa macro del lettore. Il vantaggio è che il compilatore Lisp lo vede ancora come una cosiddetta forma di primo livello - il compilatore * di file tratta specialmente i moduli di livello superiore, ad esempio aggiunge informazioni su di essi nel tempo di compilazione ambiente . Nell'esempio sopra possiamo vedere che la macro guarda nel codice sorgente ed estrae il nome.

Lisp reader legge l'esempio precedente in:

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

Che quindi espande la macro in:

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

Le macro sono molto diverse dalle macro dei lettori .

Le macro ricevono il codice sorgente, possono fare quello che vogliono e quindi restituire il codice sorgente. La sorgente di input non deve essere un codice Lisp valido. Può essere qualsiasi cosa e potrebbe essere scritto in modo totalmente diverso. Quindi il risultato deve essere un codice Lisp valido. Ma se il codice generato utilizza anche una macro, la sintassi del codice incorporato nella chiamata alla macro potrebbe nuovamente essere una sintassi diversa. Un semplice esempio: si potrebbe scrivere una macro matematica che accetta una sorta di sintassi matematica:

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

L'espressione y = 3 x ^ 2 - 4 x + 3 non è un codice Lisp valido, ma la macro potrebbe ad esempio analizzarlo e restituire un codice Lisp valido come questo:

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

Ci sono molti altri casi d'uso di macro in Lisp.

    
risposta data 11.10.2013 - 21:11
fonte
7

In Python (il linguaggio) i decoratori non possono modificare la funzione, solo avvolgerla, quindi sono decisamente molto meno potenti delle macro lisp.

In CPython (l'interprete) i decoratori possono modificare la funzione perché hanno accesso al bytecode, ma la funzione è compilata per prima e non può essere modificata dal sintetizzatore, quindi non è possibile modificare la sintassi, una cosa Lisp-macro-equivalent dovrebbe fare.

Si noti che i lisps moderni non usano le espressioni S come bytecode, quindi le macro che lavorano sugli elenchi di S-expression funzionano sicuramente prima della compilazione di bytecode come indicato sopra, in python viene eseguito da decorator.

    
risposta data 09.10.2013 - 10:42
fonte
4

È piuttosto difficile usare i decoratori Python per introdurre nuovi meccanismi di controllo del flusso.

È marginale usare le macro Common Lisp per introdurre nuovi meccanismi di controllo del flusso.

Da questo, probabilmente segue che non sono ugualmente espressivi (scelgo di interpretare "potenti" come "espressivi", poiché penso che ciò che in realtà significano).

    
risposta data 09.10.2013 - 13:32
fonte
0

È certamente correlata alla funzionalità, ma da un decoratore Python non è banale modificare il metodo chiamato (che sarebbe il parametro f nel tuo esempio). Per modificarlo, potrebbe impazzire con il modulo ast ), ma tu potrei fare una programmazione piuttosto complicata.

Le cose lungo questa linea sono state fatte però: controlla il macropy pacchetto per alcuni esempi davvero di bending.

    
risposta data 09.10.2013 - 10:37
fonte

Leggi altre domande sui tag