Patrząc na dekoratorów Pythona, ktoś stwierdził, że są tak potężni jak makra Lisp (szczególnie Clojure).
Patrząc na przykłady podane w PEP 318 , wydaje mi się, że są one po prostu fantazyjnym sposobem użycia zwykłych starych funkcji wyższego rzędu w 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):
...
Nie widziałem żadnego kodu przekształcającego się w żadnym z przykładów, jak opisano w Anatomy of a Clojure Macro . Ponadto brak homoikoniczności Pythona może uniemożliwić transformację kodu.
Jak więc porównać te dwa elementy i czy możesz powiedzieć, że są one równe pod względem tego, co możesz zrobić? Dowody wydają się przemawiać przeciwko temu.
Edycja: Na podstawie komentarza szukam dwóch rzeczy: porównania „tak potężnego jak” i „tak łatwego do robienia niesamowitych rzeczy”.
Odpowiedzi:
Dekorator jest w zasadzie tylko funkcja .
Przykład w Common Lisp:
Powyżej funkcja jest symbolem (który zostałby zwrócony przez
DEFUN
) i umieszczamy atrybuty na liście właściwości symbolu .Teraz możemy napisać to wokół definicji funkcji:
Jeśli chcemy dodać fantazyjną składnię, jak w Pythonie, piszemy makro czytnika . Makro czytnika pozwala nam programować na poziomie składni s-expression:
Następnie możemy napisać:
Czytnik Lisp czyta powyżej, aby:
Teraz mamy formę dekoratorów w Common Lisp.
Łączenie makr i makr czytelników.
Właściwie zrobiłbym powyższe tłumaczenie w prawdziwym kodzie przy użyciu makra, a nie funkcji.
Zastosowanie jest takie samo jak powyżej w przypadku tego samego makra czytnika. Zaletą jest to, że kompilator Lisp nadal postrzega go jako tak zwaną formę najwyższego poziomu - kompilator plików * traktuje formularze najwyższego poziomu specjalnie, na przykład dodaje informacje o nich do środowiska czasu kompilacji . W powyższym przykładzie widzimy, że makro przegląda kod źródłowy i wyodrębnia nazwę.
Czytnik Lisp wczytuje powyższy przykład na:
Który następnie zostaje makro rozszerzony do:
Makra bardzo różnią się od makr czytników .
Makra otrzymują kod źródłowy, mogą robić, co chcą, a następnie zwracają kod źródłowy. Źródło wejściowe nie musi być poprawnym kodem Lisp. Może to być wszystko i może być napisane zupełnie inaczej. Wynik musi wtedy być poprawnym kodem Lisp. Ale jeśli wygenerowany kod również korzysta z makra, wówczas składnia kodu osadzonego w wywołaniu makra może znów mieć inną składnię. Prosty przykład: można napisać makro matematyczne, które akceptuje pewną składnię matematyczną:
Wyrażenie
y = 3 x ^ 2 - 4 x + 3
nie jest poprawnym kodem Lisp, ale makro może na przykład parsować je i zwrócić prawidłowy kod Lisp w następujący sposób:Istnieje wiele innych przypadków użycia makr w Lisp.
źródło
W Pythonie (języku) dekoratorzy nie mogą modyfikować funkcji, tylko ją zawijają, więc są zdecydowanie mniej wydajni niż makra lisp.
W CPython (interpreter) dekoratorzy mogą modyfikować funkcję, ponieważ mają dostęp do kodu bajtowego, ale funkcja jest najpierw kompilowana, a następnie dekorator może nią manipulować, więc nie można zmienić składni, co jest makropoleceniem - odpowiednik będzie musiał zrobić.
Zauważ, że współczesne lisps nie używają wyrażeń S jako kodu bajtowego, więc makra działające na listach wyrażeń S zdecydowanie działają przed kompilacją kodu bajtowego, jak wspomniano powyżej, w Pythonie dekorator działa po nim.
źródło
Trudno jest użyć dekoratorów Python do wprowadzenia nowych mechanizmów kontroli przepływu.
Użycie makr Common Lisp do wprowadzenia nowych mechanizmów sterowania przepływem jest banalne.
Z tego prawdopodobnie wynika, że nie są one tak samo ekspresyjne (wybieram interpretowanie słowa „potężny” jako „ekspresyjny”, ponieważ uważam, że to, co faktycznie oznaczają).
źródło
s/quite hard/impossible/
Jest to z pewnością związana funkcjonalność, ale modyfikator wywoływanej metody w Pythonie nie jest trywialny (byłby to
f
parametr w twoim przykładzie). Aby zmodyfikować go pan mógł zwariować z ast modułu), ale chcesz być na jakimś dość skomplikowanego programowania.Sprawy w tym zakresie zostały jednak zrobione: sprawdź pakiet makropy , aby zobaczyć naprawdę zadziwiające przykłady.
źródło
ast
rzeczy -transformujące w pythonie nie są równe makrom Lisp. W Pythonie językiem źródłowym powinien być Python, w przypadku makr Lisp językiem źródłowym przekształconym przez makro może być dosłownie wszystko. Dlatego metaprogramowanie w języku Python nadaje się tylko do prostych rzeczy (takich jak AoP), podczas gdy metaprogramowanie Lisp jest przydatne do implementowania wydajnych kompilatorów eDSL.