Python dekoratörleri ve Lisp makroları


18

Python dekoratörlerine bakarken birisi Lisp makroları (özellikle Clojure) kadar güçlü olduklarını ifade etti.

PEP 318'de verilen örneklere baktığımda bana Lisp'teki eski düz üst düzey işlevleri kullanmanın sadece süslü bir yolu gibi bakıyorlar:

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):
    ...

Clojure Macro Anatomisi'nde anlatıldığı gibi, hiçbir örnekte kod dönüşümü görmedim . Artı, Python'un eksik homoiconicity olabilir kod dönüşümleri imkansız hale getirmektedir.

Peki, bu ikisi nasıl karşılaştırıyor ve ne yapabileceğiniz konusunda eşit olduklarını söyleyebilir misiniz? Kanıt buna karşı geliyor gibi görünüyor.

Edit: Bir yorum dayanarak, ben iki şey arıyorum: karşılaştırma "kadar güçlü" ve "kadar harika şeyler yapmak kadar kolay".


12
Elbette dekoratörler gerçek makro değildir. Rastgele bir dili (tamamen farklı bir sözdizimi ile) python'a çeviremezler. Aksini iddia edenler sadece makroları anlamıyorlar.
SK-logic

1
Python homoik değildir, ancak çok dinamiktir. Homoiconicity yalnızca derleme zamanında yapmak istiyorsanız güçlü kod dönüşümleri için gereklidir - derlenmiş AST'ye ve onu değiştirmek için araçlara doğrudan erişim desteğiniz varsa, dil sözdizimine bakılmaksızın çalışma zamanında yapabilirsiniz. Bununla birlikte, "kadar güçlü" ve "harika şeyler yapmak kadar kolay" çok farklı kavramlardır.
Phoshi

Belki de soruyu “harika şeyler yapmak kadar kolay” olarak değiştirmeliyim. ;)
Profpatsch

Belki birisi yukarıdaki Python örneğine benzer bazı Clojure üst düzey işlevlerini hackleyebilir. Denedim ama bu süreçte aklımdan geçtim. Python örneği nesne öznitelikleri kullandığından, bunun biraz farklı olması gerekir.
Profpatsch

@Phoshi Derlenen AST'nin çalışma zamanında değiştirilmesi şu şekilde bilinir: kendini değiştiren kod .
Kaz

Yanıtlar:


16

Bir dekoratör temelde sadece bir işlevdir .

Common Lisp Örneği:

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

Yukarıda işlev bir sembol (tarafından döndürülecek DEFUN) ve özellikleri sembolün özellik listesine koyduk .

Şimdi bunu bir fonksiyon tanımının etrafına yazabiliriz:

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

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

)  

Python'da olduğu gibi süslü bir sözdizimi eklemek istiyorsak, bir okuyucu makrosu yazarız . Bir okuyucu makrosu, s-ifade sözdizimi düzeyinde programlamamızı sağlar:

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

Sonra yazabiliriz:

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

Lisp okuyucusu yukarıda şunları okur:

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

Şimdi Common Lisp'te bir çeşit dekoratör var .

Makroları ve okuyucu makroları birleştirme.

Aslında bir işlevi değil, bir makro kullanarak gerçek kod yukarıdaki çeviri yapardı.

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

Aynı okuyucu makrosu ile kullanım yukarıdaki gibidir. Avantajı, Lisp derleyicisinin hala üst düzey form olarak görmesidir - * dosya derleyicisi üst düzey formlara özel davranır, örneğin derleme zamanı ortamına onlar hakkında bilgi ekler . Yukarıdaki örnekte, makronun kaynak koduna baktığını ve adı çıkardığını görebiliriz.

Lisp okuyucu yukarıdaki örneği şu şekilde okur:

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

Hangi makro genişletilir alır:

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

Makrolar okuyucu makrolardan çok farklıdır .

Makrolar kaynak kodunu geçirir, istediklerini yapabilir ve sonra kaynak kodunu döndürebilir. Giriş kaynağının geçerli Lisp kodu olması gerekmez. Herhangi bir şey olabilir ve tamamen farklı yazılabilir. Sonuç geçerli Lisp kodu olmalıdır. Ancak, oluşturulan kod da bir makro kullanıyorsa, makro çağrısında gömülü kodun sözdizimi yine farklı bir sözdizimi olabilir. Basit bir örnek: bir çeşit matematik sözdizimini kabul edecek bir matematik makrosu yazılabilir:

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

İfade y = 3 x ^ 2 - 4 x + 3geçerli Lisp kodu değil, ancak makro örneğin ayrıştırıp geçerli Lisp kodunu şu şekilde döndürebilir:

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

Lisp'te başka birçok makro kullanımı vardır.


8

Python'da (dil) dekoratörler işlevi değiştiremez, sadece sarar, bu yüzden lisp makrolarından kesinlikle çok daha az güçlüdürler.

CPython'da (yorumlayıcı) dekoratörler, bayt koduna erişebildikleri için işlevi değiştirebilir, ancak işlev önce derlenir ve dekoratör tarafından değiştirilebileceğinden, sözdizimini değiştirmek mümkün değildir, lisp-makro - buna eşdeğer olmalı.

Modern lispslerin S-ifadelerini bayt kodu olarak kullanmadığına dikkat edin, bu nedenle S-ifade listelerinde çalışan makrolar, yukarıda belirtildiği gibi bayt kodu derlemesinden önce çalışır, dekoratör ondan sonra çalışır.


1
İşlevi değiştirmeniz gerekmez. Sadece fonksiyonun kodunu bir biçimde okumalısınız (pratikte bu bytecode anlamına gelir). Bu onu daha pratik hale getirmez.

2
@delnan: Teknik olarak lisp de değiştirmiyor; yeni bir kaynak oluşturmak için kaynak olarak kullanıyor ve python da evet. Sorun, jeton listesinin veya AST'nin bulunmaması ve derleyicinin makroda aksi halde izin verebileceğiniz bazı şeylerden zaten şikayet etmesidir.
Jan Hudec

4

Yeni kontrol akış mekanizmalarını tanıtmak için Python dekoratörlerini kullanmak oldukça zordur.

Yeni kontrol akış mekanizmalarını tanıtmak için Common Lisp makrolarını kullanmak önemsizdir.

Bundan da, muhtemelen eşit derecede etkileyici olmadıklarını takip eder ("güçlü" kelimeyi "ifade" olarak yorumlamayı seçerim, çünkü aslında ne anlama geldiğini düşünüyorum).


Diyorum cesarets/quite hard/impossible/

@delnan Eh, gitmezdim oldukça şimdiye kadar "imkansız" demek olarak, ama kesinlikle ona çalışmalarında gerekirdi.
Vatikan

0

Kesinlikle işlevsellik ile ilgilidir, ancak bir Python dekoratöründen, çağrılan yöntemi değiştirmek önemsiz değildir (bu f, örneğinizdeki parametre olacaktır ). Bunu değiştirmek için size olabilir ile deliriyorum ast ) modülü, ancak bazı oldukça karmaşık programlama için olurdunuz.

Bu hat boyunca işler yapılmıştır: gerçekten akıllara durgunluk veren bazı örnekler için macropy paketine bakın.


3
astPython'daki -transforming şeyler bile Lisp makrolarına eşit değildir. Python ile kaynak dil Python olmalıdır, Lisp makroları ile bir makro tarafından dönüştürülen kaynak dil, kelimenin tam anlamıyla her şey olabilir. Bu nedenle, Python meta programlaması sadece basit şeyler (AoP gibi) için uygundur, Lisp meta programlaması ise güçlü eDSL derleyicilerini uygulamak için kullanışlıdır.
SK-logic

1
Şey macropy olmasıdır değil dekoratörler kullanarak uyguladı. Dekoratör sözdizimini kullanır (çünkü geçerli bir python sözdizimi kullanmalıdır), ancak bayt derleme işlemini bir alma kancasından ele geçirerek uygulanır.
Jan Hudec

@ SK-logic: Lisp'de kaynak dilin lisp olması gerekir. Sadece Lisp sözdizimi çok basit ama esnektir, python sözdizimi ise çok daha karmaşık ve çok esnek değildir.
Jan Hudec

1
@ JanHudec, Lisp kaynak dilinde herhangi bir (gerçekten, herhangi bir ) sözdizimine sahip olabilir - bkz. Okuyucu makroları.
SK-logic
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.