파이썬 데코레이터와 Lisp 매크로


18

파이썬 데코레이터를 볼 때 누군가가 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 Macro의 Anatomy에 설명 된 것처럼 예제에서 코드 변환을 보지 못했습니다 . 또한, 파이썬의 누락 동질성 코드 변환을 불가능하게 수 있습니다.

자,이 두 가지를 어떻게 비교하고 당신이 할 수있는 일이 같다고 말할 수 있습니까? 증거는 그것을 반대하는 것으로 보인다.

편집 : 의견을 바탕으로 두 가지를 찾고 있습니다.“강력한”과“굉장한 일을 쉽게 할 수있는”에 대한 비교.


12
물론 데코레이터는 실제 매크로가 아닙니다. 그들은 완전히 다른 문법을 가진 임의의 언어를 파이썬으로 번역 할 수 없습니다. 반대를 주장하는 사람들은 단순히 매크로를 이해하지 못합니다.
SK-logic

1
파이썬은 호모 닉이 아니지만 매우 역동적입니다. 컴파일 타임에 코드를 변환하려는 경우 강력한 코드 변환에만 호모 닉성이 필요합니다. 컴파일 된 AST에 직접 액세스하고이를 변경할 수있는 도구가있는 경우 언어 구문에 관계없이 런타임에 수행 할 수 있습니다. "강력한"과 "멋진 일을하기 쉬운"이라는 개념은 매우 다른 개념입니다.
Phoshi

어쩌면 나는 그 질문을“멋진 일을하기 쉬운 것”으로 바꿔야 할 것입니다. ;)
Profpatsch

어쩌면 누군가가 위의 Python 예제와 비슷한 Clojure 고차 함수를 해킹 할 수 있습니다. 나는 그 과정에서 내 마음을 열렬히 교차시켰다. 파이썬 예제는 객체 속성을 사용하기 때문에 약간 달라야합니다.
Profpatsch

@Phoshi 런타임에 컴파일 된 AST를 변경하는 것을자가 수정 코드라고 합니다.
Kaz

답변:


16

장식은 기본적으로 단지입니다 기능 .

일반적인 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))

)  

파이썬에서와 같이 멋진 구문을 추가하려면 리더 매크로를 작성하십시오 . 리더 매크로를 사용하면 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))

Lisp 리더는 다음을 읽습니다.

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

이제 Common Lisp 에 데코레이터 형식이 있습니다.

매크로와 리더 매크로의 결합.

실제로 함수가 아닌 매크로를 사용하여 실제 코드에서 위의 번역을 수행합니다.

(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 컴파일러는 최상위 양식을 특수하게 처리합니다 (예 : 컴파일 타임 환경 에 해당 양식에 대한 정보 추가) . 위의 예에서 매크로는 소스 코드를보고 이름을 추출 함을 알 수 있습니다.

Lisp 리더 는 위 예제를 다음과 같이 읽습니다.

(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에는 많은 다른 매크로 사용 사례가 있습니다.


8

파이썬 (언어)에서 데코레이터는 함수를 수정할 수없고, 랩핑 만하기 때문에 lisp 매크로보다 훨씬 강력하지는 않습니다.

CPython (인터프리터)에서 데코레이터는 바이트 코드에 액세스 할 수 있기 때문에 함수를 수정할 수 있지만 함수는 먼저 컴파일되어 데코레이터가 뒤섞 일 수 있으므로 구문을 변경할 수 없습니다. 동등한 작업을 수행해야합니다.

현대 리스프는 S- 표현식을 바이트 코드로 사용하지 않으므로 S- 표현 목록에서 작업하는 매크로는 위에서 언급 한 것처럼 바이트 코드 컴파일 전에 작동합니다. 파이썬에서는 데코레이터가 그 뒤를 실행합니다.


1
기능을 수정할 필요가 없습니다. 함수 코드는 특정 형식으로 만 읽으면됩니다 (실제로 이것은 바이트 코드를 의미 함). 이것이 더 실용적이지는 않습니다.

2
@delnan : 기술적으로 lisp도 수정하지 않습니다. 새로운 소스를 생성하기 위해 소스로 사용하고 있으므로 파이썬도 마찬가지입니다. 문제는 토큰 목록 또는 AST가없고 컴파일러가 매크로에서 허용 할 수있는 몇 가지 사항에 대해 이미 불평 한 사실에 있습니다.
Jan Hudec

4

파이썬 데코레이터를 사용하여 새로운 제어 흐름 메커니즘을 도입하는 것은 매우 어렵습니다.

새로운 제어 흐름 메커니즘을 도입하기 위해 Common Lisp 매크로를 사용하는 것은 쉽지 않습니다.

이것으로부터 그들은 아마도 똑같이 표현 적이 지 않을 것입니다 (나는 그들이 실제로 의미하는 바를 생각하기 때문에 "강력한"을 "표현"으로 해석하기로 선택합니다).


감히s/quite hard/impossible/

@delnan 글쎄, 난 안 갈거야 확실히 지금까지 "불가능"말에 관해서는,하지만 당신은 확실히에서 작업해야 할 것이다.
Vatine

0

그것은 분명히 기능과 관련이 있지만, 파이썬 데코레이터에서 호출되는 메소드를 수정하는 것은 쉽지 않습니다 (이것은 f예제 의 매개 변수 일 것입니다 ). 그것을 수정하려면 ast 모듈에 열중 할 있지만) 매우 복잡한 프로그래밍에 빠질 것입니다.

이 행을 따라 작업이 완료되었습니다. 매크로 파이 패키지에서 정말 마음에 드는 예제를 확인하십시오.


3
ast파이썬 에서 -transforming 항목 조차도 Lisp 매크로와 같지 않습니다. Python의 경우 소스 언어는 Python이어야하며 Lisp 매크로의 경우 매크로로 변환 된 소스 언어는 문자 그대로 모든 것이 될 수 있습니다. 따라서 Python 메타 프로그래밍은 단순한 것 (AoP 등)에만 적합하고 Lisp 메타 프로그래밍은 강력한 eDSL 컴파일러를 구현하는 데 유용합니다.
SK-logic

1
문제는 데코레이터를 사용하여 매크로를 구현 하지 않는다는 것입니다 . 데코레이터 구문을 사용하지만 (유효한 파이썬 구문을 사용해야하기 때문에) 가져 오기 후크에서 바이트 컴파일 프로세스를 하이재킹하여 구현됩니다.
Jan Hudec

@ SK-logic : Lisp에서 소스 언어도 lisp해야합니다. Lisp 구문은 매우 간단하지만 유연하지만 Python 구문은 훨씬 복잡하고 유연하지 않습니다.
Jan Hudec

1
@JanHudec은 리스프 소스 언어 중 하나를 가질 수 있습니다 (정말 의미, 어떤 ) 구문 - 리더 매크로를 참조하십시오.
SK-logic
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.