web-dev-qa-db-ja.com

PythonデコレータとLISPマクロ

Pythonデコレータを見るとき、誰かがLISPマクロ(特にClojure)と同じくらい強力であることを表明しました。

PEP 318 に示されている例を見ると、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):
    ...

Clojureマクロの構造 で説明されているように、どの例でもコードが変換されることはありません。さらに、Pythonの欠落 homoiconicitycouldはコード変換を不可能にします。

では、これら2つをどのように比較して、あなたができることはほぼ同じだと言えますか?証拠はそれに反対するようです。

編集:コメントに基づいて、次の2つを探しています。「と同じくらい強力」と「簡単に素晴らしいことを実行する」の比較」。

18
Profpatsch

Adecoratorは、基本的にはfunction =

一般的なLISPの例:

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

上記では、関数はシンボル(DEFUNによって返される)であり、シンボルのプロパティリストに属性を配置します。

これで関数定義の周りにそれを書くことができます:

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

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

)  

Pythonのように豪華な構文を追加したい場合は、リーダーマクロを記述します。リーダーマクロを使用すると、s-expression構文のレベルでプログラムできます。

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

次に、次のように書きます。

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

LISPreaderは上記を読み取り、次のようにします。

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

これで、Common LISPにdecoratorsという形式ができました。

マクロとリーダーマクロを組み合わせる。

実際には、関数ではなくマクロを使用して、実際のコードで上記の変換を行います。

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

使用方法は上記と同じリーダーマクロです。利点は、LISPコンパイラがそれをいわゆるトップレベルフォームと見なすことです-* fileコンパイラは、トップレベルフォームを特別に扱います。例は、それらに関する情報をコンパイル時環境に追加します。上記の例では、マクロがソースコードを調べて名前を抽出していることがわかります。

LISPreaderは、上記の例を次のように読み取ります。

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

マクロは次のように展開されます:

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

マクロはリーダーマクロとは大きく異なります)

マクロは、ソースコードを渡し、必要な処理を行ってからソースコードを返すことができます。入力ソースは有効なLISPコードである必要はありません。それは何でもかまいませんし、まったく異なるように書くこともできます。結果は有効なLISPコードでなければなりません。しかし、生成されたコードもマクロを使用している場合、マクロ呼び出しに埋め込まれたコードの構文は、異なる構文になる可能性があります。簡単な例:ある種の数学構文を受け入れる数学マクロを書くことができます:

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

表現 y = 3 x ^ 2 - 4 x + 3は有効なLISPコードではありませんが、マクロはそれを解析して、次のような有効なLISPコードを返すことができます。

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

LISPには、他にも多くのマクロの使用例があります。

16
Rainer Joswig

Python(言語))では、デコレータは関数を変更できず、ラップするだけなので、LISPマクロよりもはるかに強力ではありません。

CPython(インタープリター)では、デコレーターはバイトコードにアクセスできるため、関数を変更できますが、関数は最初にコンパイルされ、デコレーターでいじることができるため、LISPマクロの構文を変更することはできません。 -同等のものを行う必要があります。

現代のlispsはバイトコードとしてS式を使用しないことに注意してください。したがって、S式リストで機能するマクロは、上記のようにバイトコードのコンパイル前に確実に機能します。python.

8
Jan Hudec

Pythonデコレータを使用して新しい制御フローメカニズムを導入するのは非常に困難です。

Common LISPマクロを使用して新しい制御フローメカニズムを導入することは簡単です。

このことから、おそらくそれらは同等に表現力がない(おそらく「強力」は「表現力がある」と解釈することを選択します。

4
Vatine

これは確かに関連する機能ですが、Pythonデコレータから、呼び出されるメソッドを変更することは簡単ではありません(例ではfパラメータになります)。それを変更するにはcouldast モジュールに夢中になりますが)かなり複雑なプログラミングに参加することになります。

この線に沿った処理は行われていますが、 macropy パッケージをチェックして、本当に心を痛める例をいくつか確認してください。

0
Jacob Oscarson