Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 318 – 함수와 메서드를 위한 데코레이터

Author:
Kevin D. Smith <Kevin.Smith at theMorgue.org>, Jim J. Jewett, Skip Montanaro, Anthony Baxter
Status:
Final
Type:
Standards Track
Created:
05-Jun-2003
Python-Version:
2.4
Post-History:
09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004, 30-Aug-2004, 02-Sep-2004

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

WarningWarningWarning

이 문서는 데코레이터 문법과, 그에 관한 결정에 이르기까지의 과정을 설명하기 위한 것입니다. 이 문서는 가능한 수많은 대안 문법을 모두 다루려는 것이 아니며, 각 형식의 장단점을 빠짐없이 나열하려는 것도 아닙니다.

초록

함수와 메서드를 변환하는 현재 방법(예를 들어 클래스 메서드나 정적 메서드로 선언하는 것)은 불편하며, 이해하기 어려운 코드로 이어질 수 있습니다. 이상적으로는 이러한 변환이 코드에서 선언 자체가 이루어지는 바로 그 지점에서 이루어져야 합니다. 이 PEP는 함수 또는 메서드 선언을 변환하기 위한 새로운 문법을 도입합니다.

동기

함수나 메서드에 변환을 적용하는 현재 방법은 실제 변환을 함수 본문 뒤에 둡니다. 큰 함수에서는 이 때문에 함수 동작의 핵심 요소가 함수의 나머지 외부 인터페이스 정의와 떨어지게 됩니다. 예를 들면 다음과 같습니다.:

def foo(self):
    perform method operation
foo = classmethod(foo)

메서드가 길어질수록 가독성은 더 떨어집니다. 개념적으로 하나의 선언에 불과한 것을 위해 함수 이름을 세 번이나 적는 것도 그다지 파이썬답지 않아 보입니다. 이 문제의 해결책은 메서드의 변환을 메서드 자신의 선언에 더 가깝게 옮기는 것입니다. 새로운 문법의 의도는 다음 코드를

def foo(cls):
    pass
foo = synchronized(lock)(foo)
foo = classmethod(foo)

함수 선언 안에 데코레이션을 두는 다음과 같은 대안으로 대체하는 것입니다.:

@classmethod
@synchronized(lock)
def foo(cls):
    pass

이러한 방식으로 클래스를 수정하는 것도 가능하지만, 그 이점은 즉시 드러나지는 않습니다. 클래스 데코레이터로 할 수 있는 일은 거의 확실히 메타클래스로도 할 수 있지만, 메타클래스를 사용하는 것은 상당히 난해하므로 클래스를 간단하게 수정할 수 있는 더 쉬운 방법에는 어느 정도 매력이 있습니다. Python 2.4에서는 함수/메서드 데코레이터만 추가됩니다.

PEP 3129는 Python 2.6부터 클래스 데코레이터를 추가할 것을 제안합니다.

이것이 왜 이렇게 어려운 것입니까?

두 데코레이터(classmethod()와 staticmethod())는 버전 2.2부터 Python에서 사용할 수 있었습니다. 대략 그 무렵부터 이들을 위한 어떤 문법적 지원이 결국 언어에 추가될 것이라고 여겨져 왔습니다. 이러한 가정을 고려하면 합의에 이르는 것이 왜 그토록 어려웠는지 궁금할 수 있습니다. 함수 데코레이터를 어떻게 구현하는 것이 가장 좋은지를 두고 comp.lang.python과 python-dev 메일링 리스트 양쪽에서 때때로 논쟁이 격렬하게 일었다가 잦아들기를 반복했습니다. 왜 그래야 하는지에 대한 하나의 명확한 이유는 없지만, 몇 가지 문제가 가장 크게 의견을 갈라놓는 것으로 보입니다.

  • “의도 선언”을 어디에 두어야 하는지에 대한 의견 불일치. 함수 정의의 끝에서 함수를 데코레이트/변환하는 것이 최선이 아니라는 데에는 거의 모두가 동의합니다. 그 외에는 이 정보를 어디에 두어야 할지에 대한 명확한 합의가 없는 것으로 보입니다.
  • 구문상의 제약. Python은 구문이 단순한 언어이며, “일을 엉망으로 만들지” 않고 할 수 있는 것과 할 수 없는 것에 대해 (시각적으로나 언어 파서 측면에서나) 상당히 강한 제약이 있습니다. 이 개념을 처음 접하는 사람이 “아, 그렇군요, 무엇을 하려는지 알겠습니다”라고 생각하게 만들 만큼 이 정보를 구조화할 뻔한 방법은 없습니다. 기대할 수 있는 최선은 새로운 사용자가 이 문법의 의미에 대해 크게 잘못된 멘탈 모델을 만들지 않도록 하는 것입니다.
  • 개념 자체에 대한 전반적인 생소함. 대수학(또는 기초 산술만이라도)을 조금이라도 알거나 다른 프로그래밍 언어를 하나 이상 써 본 사람에게 Python의 많은 부분은 직관적입니다. Python에서 데코레이터 개념을 접하기 전에 이를 경험해 본 사람은 거의 없을 것입니다. 이 개념을 담아내는, 이미 널리 자리 잡은 강력한 밈이 없을 뿐입니다.
  • 일반적으로 문법에 관한 논의는 다른 거의 어떤 주제보다도 더 많은 논쟁을 불러일으키는 것으로 보입니다. 이에 대한 또 다른 예로는 PEP 308과 관련된 삼항 연산자 논의를 참고하십시오.

배경

현재 상황보다는 구문적 지원이 바람직하다는 데 대체로 의견이 일치합니다. Guido는 10th Python Conference의 DevDay 기조연설에서 데코레이터에 대한 문법적 지원(syntactic support for decorators)을 언급했지만, 나중에 그것은 그 자리에서 “반쯤 농담으로” 제안한 여러 확장 중 하나일 뿐이었다고 말했습니다(he later said). 콘퍼런스 직후 Michael Hudson이 python-dev에서 이 주제를 제기했으며(Michael Hudson raised the topic), 최초의 대괄호 문법은 앞서 Gareth McCaughan이 comp.lang.python에 올린 제안에서 비롯되었다고 밝혔습니다.

클래스 정의와 함수 정의는 구문적으로 비슷하므로 클래스 데코레이션은 자연스러운 다음 단계로 보이지만, Guido는 여전히 확신하지 못하고 있으며 클래스 데코레이터는 거의 확실히 Python 2.4에 포함되지 않을 것입니다.

논의는 2002년 2월부터 2004년 7월까지 python-dev에서 간헐적으로 이어졌습니다. 수백, 수백 개의 게시물이 올라왔고, 사람들은 가능한 여러 문법 변형을 제안했습니다. Guido는 제안 목록을 EuroPython 2004에 가져갔고, 그곳에서 논의가 이루어졌습니다. 그 후 그는 Java-style @decorator 문법을 채택하기로 결정했고, 이 문법은 2.4a2에서 처음 등장했습니다. Barry Warsaw는 데코레이터 문법과 비슷한 시기에 열린 Pie-thon Parrot 대결을 기념하고, 또 @가 파이처럼 조금 생겼다는 이유로 이를 ‘pie-decorator’ 문법이라고 이름 붙였습니다. Guido는 Python-dev에서 outlined his case했으며, 여기에는 (수많은) 기각된 형식 중 일부에 대한 this piece도 포함되어 있습니다.

‘데코레이터’라는 이름에 대하여

이 기능의 이름으로 ‘데코레이터’를 선택한 것에 대해 여러 불만이 제기되었습니다. 주된 불만은 이 이름이 GoF book에서의 용례와 일치하지 않는다는 것입니다. ‘decorator’(데코레이터)라는 이름은 아마도 컴파일러 분야에서의 쓰임에서 더 많이 유래했을 것입니다 – 문법 트리를 순회하면서 어노테이션을 붙이는 것입니다. 더 나은 이름이 나타날 가능성도 충분히 있습니다.

설계 목표

새 문법은 다음 조건을 만족해야 합니다.

  • 사용자 정의 호출 가능 객체와 기존 내장 함수인 classmethod()와 staticmethod()를 포함하여 임의의 래퍼에 대해 동작해야 합니다. 이 요구 사항은 또한 데코레이터 문법이 래퍼 생성자에 인자를 전달하는 것을 지원해야 함을 의미합니다
  • 하나의 정의에 여러 래퍼를 사용할 수 있어야 합니다
  • 무슨 일이 일어나는지 명확히 드러나야 합니다. 최소한 새 사용자가 자신의 코드를 작성할 때 이를 무시해도 안전하다는 점은 분명해야 합니다
  • “한 번 설명을 들으면 … 기억하기 쉬운” 문법이어야 합니다
  • 향후 확장을 더 어렵게 만들지 않아야 합니다
  • 입력하기 쉬워야 합니다. 이 기능을 사용하는 프로그램은 이를 매우 자주 사용할 것으로 예상됩니다
  • 코드를 빠르게 훑어보기 더 어렵게 만들지 않아야 합니다. 모든 정의, 특정 정의, 또는 함수가 받는 인자를 여전히 쉽게 검색할 수 있어야 합니다
  • 언어 인식 편집기나 그 밖의 “toy parser tools out there” 같은 보조 지원 도구를 불필요하게 복잡하게 만들지 않아야 합니다
  • 향후 컴파일러가 데코레이터를 최적화할 수 있도록 해야 합니다. 언젠가 Python용 JIT 컴파일러가 등장하기를 기대한다면, 이는 데코레이터 문법이 함수 정의보다 앞에 와야 한다는 요구로 이어지는 경향이 있습니다
  • 현재 숨겨져 있는 함수의 끝에서 더 눈에 잘 띄는(in your face) 앞쪽으로 옮겨야 합니다

Andrew Kuchling은 자신의 블로그(in his blog)에 동기와 사용 사례에 관한 여러 논의의 링크를 모아 두었습니다. 특히 주목할 만한 것은 Jim Huginin’s list of use cases입니다.

현재 문법

Python 2.4a2에 구현된 함수 데코레이터의 현재 문법은 다음과 같습니다:

@dec2
@dec1
def func(arg1, arg2, ...):
    pass

이는 다음과 동등합니다:

def func(arg1, arg2, ...):
    pass
func = dec2(dec1(func))

단, 변수 func에 대한 중간 대입은 없습니다. 데코레이터는 함수 선언 가까이에 있습니다. @ 기호는 여기서 무언가 새로운 일이 일어나고 있음을 분명히 보여 줍니다.

적용 순서(order of application, 아래에서 위로)의 근거는 그것이 일반적인 함수 적용 순서와 일치한다는 점입니다. 수학에서 함수의 합성 (g o f)(x)는 g(f(x))로 옮겨집니다. Python에서 @g @f def foo()는 foo=g(f(foo)로 옮겨집니다.

데코레이터 문이 받아들일 수 있는 것에는 제한이 있으며, 임의의 표현식은 동작하지 않습니다. Guido는 직감(gut feeling) 때문에 이 방식을 선호했습니다.

현재 문법은 데코레이터 선언에서 데코레이터를 반환하는 함수를 호출하는 것도 허용합니다:

@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
    pass

이는 다음과 동등합니다:

func = decomaker(argA, argB, ...)(func)

데코레이터를 반환하는 함수를 두는 근거는, @ 기호 뒤의 부분을 (구문상으로는 함수로만 제한되지만) 하나의 표현식으로 볼 수 있으며, 그 표현식이 반환하는 것이 무엇이든 호출된다는 점입니다. declaration arguments를 참조하십시오.

문법 대안

서로 다른 문법이 매우 많이(a large number) 제안되었습니다. 이 문법들을 하나하나 검토하기보다는 문법 논의를 여러 영역으로 나누어 살펴보는 것이 유익합니다. 가능한 각 구문(each possible syntax)을 개별적으로 논의하려는 시도는 미친 짓이며, 완전히 다루기 힘든 PEP를 만들어 낼 것입니다.

데코레이터 위치

문법의 첫 번째 쟁점은 데코레이터의 위치입니다. 다음 예제에서는 2.4a2에서 사용된 @구문을 사용합니다.

첫 번째 대안은 데코레이터를 def 문 앞에 두는 것으로, 2.4a2에서 사용된 문법입니다:

@classmethod
def foo(arg1,arg2):
    pass

@accepts(int,int)
@returns(float)
def bar(low,high):
    pass

이 위치에 대해서는 여러 반대 의견이 제기되었습니다. 가장 주된 반대 의견은 이것이 코드 한 줄이 다음 줄에 영향을 미치는 사실상 최초의 Python 사례라는 점입니다. 2.4a3에서 사용할 수 있는 문법은 한 줄에 데코레이터 하나만 허용하며(a2에서는 여러 데코레이터를 같은 줄에 지정할 수 있었습니다), 2.4 최종 릴리스에서도 한 줄에 데코레이터 하나라는 결정이 최종적으로 유지되었습니다.

여러 데코레이터를 사용하면 문법이 금세 다루기 어려워진다는 불만도 있었습니다. 하지만 하나의 함수에 많은 수의 데코레이터가 사용될 가능성은 낮으므로 크게 걱정할 일은 아니라는 지적도 있었습니다.

이 형식의 장점 중 하나는 데코레이터가 메서드 본문 바깥에 있다는 점입니다. 따라서 데코레이터가 함수가 정의되는 시점에 실행된다는 것이 분명합니다.

또 다른 장점은 함수 정의 앞에 붙는 접두어가 코드 자체보다 먼저 코드의 의미 변화를 알게 된다는 생각에 부합한다는 점입니다. 따라서 문법이 함수 정의보다 앞에 오지 않았다면 되돌아가서 처음의 인식을 바꿔야 했겠지만, 그럴 필요 없이 코드의 의미를 올바르게 해석하는 방법을 알 수 있습니다.

인자 목록이 길면 데코레이터가 ‘가려질’ 것이라는 우려 때문에, Guido는 데코레이터를 ‘def’ 앞 줄에 두는 방식을 선호한다고(he preferred) 결정했습니다

두 번째 형식은 데코레이터를 def와 함수 이름 사이, 또는 함수 이름과 인자 목록 사이에 두는 것입니다.:

def @classmethod foo(arg1,arg2):
    pass

def @accepts(int,int),@returns(float) bar(low,high):
    pass

def foo @classmethod (arg1,arg2):
    pass

def bar @accepts(int,int),@returns(float) (low,high):
    pass

이 형식에는 몇 가지 반대 의견이 있습니다. 첫 번째는 소스의 ‘grep 용이성’을 쉽게 깨뜨린다는 점입니다. 더 이상 ‘def foo(‘를 검색하여 함수의 정의를 찾을 수 없습니다. 두 번째이자 더 심각한 반대 의견은 데코레이터가 여러 개인 경우 문법이 극도로 다루기 어려워진다는 점입니다.

다음 형식은 여러 강력한 지지자가 있었던 것으로, 데코레이터를 ‘def’ 줄의 인자 목록과 끝의 : 사이에 두는 것입니다.:

def foo(arg1,arg2) @classmethod:
    pass

def bar(low,high) @accepts(int,int),@returns(float):
    pass

Guido는 이 형식에 반대하는 논거를(그중 상당수는 앞의 형식에도 해당합니다) 다음과 같이 정리했습니다(summarized the arguments):

  • 중요한 정보(예: 정적 메서드라는 사실)를 시그니처 뒤에 숨기므로 놓치기 쉽습니다.
  • 긴 인자 목록과 긴 데코레이터 목록 사이의 경계를 놓치기 쉽습니다.
  • 데코레이터 목록이 줄 중간에서 시작하고 끝나기 때문에, 재사용을 위해 잘라 내어 붙여 넣기가 번거롭습니다.

다음 형식은 데코레이터 문법을 메서드 본문 안의 시작 부분, 즉 현재 독스트링이 위치하는 곳에 두는 것입니다.:

def foo(arg1,arg2):
    @classmethod
    pass

def bar(low,high):
    @accepts(int,int)
    @returns(float)
    pass

이 형식에 대한 주된 반대 의견은 데코레이터를 파악하려면 메서드 본문 “안을 들여다봐야” 한다는 점입니다. 게다가 이 코드는 메서드 본문 안에 있지만 메서드가 실행될 때 실행되지 않습니다. Guido는 독스트링이 좋은 반례가 아니라고 보았으며, ‘docstring’ 데코레이터가 독스트링을 함수 본문 바깥으로 옮기는 데 도움이 될 수도 있다고 생각했습니다.

마지막 형식은 메서드의 코드를 감싸는 새로운 블록입니다. 이 예제에서는 ‘decorate’ 키워드를 사용하겠습니다. @문법으로는 말이 되지 않기 때문입니다.

decorate:
    classmethod
    def foo(arg1,arg2):
        pass

decorate:
    accepts(int,int)
    returns(float)
    def bar(low,high):
        pass

이 형식을 사용하면 데코레이트된 메서드와 데코레이트되지 않은 메서드의 들여쓰기가 일관되지 않게 됩니다. 게다가 데코레이트된 메서드의 본문은 세 단계 들여쓰기에서 시작하게 됩니다.

문법 형식

  • @decorator:
    @classmethod
    def foo(arg1,arg2):
        pass
    
    @accepts(int,int)
    @returns(float)
    def bar(low,high):
        pass
    

    이 문법에 대한 주요 반대 의견은 @ 기호가 현재 Python에서 사용되지 않으며(IPython과 Leo에서는 모두 사용됩니다), @ 기호가 아무런 의미를 전달하지 않는다는 점입니다. 또 다른 반대 의견은 현재 사용되지 않는 (한정된 집합에 속한) 문자를 주요 용도로 여겨지지 않는 기능에 “낭비”한다는 점입니다.

  • |decorator:
    |classmethod
    def foo(arg1,arg2):
        pass
    
    |accepts(int,int)
    |returns(float)
    def bar(low,high):
        pass
    

    이것은 @decorator 문법의 변형으로, IPython과 Leo를 깨뜨리지 않는다는 장점이 있습니다. @문법과 비교했을 때 주요 단점은 | 기호가 대문자 I와 소문자 l 둘 다와 비슷해 보인다는 점입니다.

  • 리스트 문법:
    [classmethod]
    def foo(arg1,arg2):
        pass
    
    [accepts(int,int), returns(float)]
    def bar(low,high):
        pass
    

    리스트 문법에 대한 주된 반대 의견은 이 문법이 현재 이미 의미를 가진다는 점입니다(메서드 앞에 오는 형태로 사용될 때). 또한 그 표현식이 데코레이터임을 나타내는 표시가 전혀 없습니다.

  • 다른 괄호를 사용하는 리스트 문법(<...>, [[...]], …):
    <classmethod>
    def foo(arg1,arg2):
        pass
    
    <accepts(int,int), returns(float)>
    def bar(low,high):
        pass
    

    이 대안들 중 어느 것도 큰 지지를 얻지 못했습니다. 대괄호를 사용하는 대안들은 데코레이터 구문이 리스트가 아니라는 점을 분명히 보여 주는 역할만 할 뿐입니다. 구문 분석을 조금이라도 더 쉽게 만드는 데에는 아무런 도움이 되지 않습니다. ‘<…>’ 대안은 ‘<’와 ‘>’가 이미 짝을 이루지 않는 기호로 구문 분석되기 때문에 구문 분석 문제를 일으킵니다. 또한 오른쪽 꺾쇠괄호가 데코레이터를 닫는 기호가 아니라 ‘보다 크다’ 기호일 수도 있으므로 구문 분석상의 모호성이 추가로 생깁니다.

  • decorate()

    decorate() 제안은 새로운 문법을 구현하지 않고, 대신 인트로스펙션을 사용하여 뒤따르는 함수를 조작하는 마법 같은 함수를 두자는 것이었습니다. Jp Calderone과 Philip Eby 두 사람 모두 이러한 일을 하는 함수의 구현을 만들었습니다. Guido는 이에 상당히 확고하게 반대했습니다. 새로운 문법 없이는 이런 함수의 마법성이 지나치게 높다는 것이었습니다:

    sys.settraceback을 통해 “원격 작용(action-at-a-distance)”을 일으키는 함수를 사용하는 것은, 다른 방법으로는 얻을 수 없지만 언어를 변경할 만한 가치는 없는 잘 쓰이지 않는 기능이라면 괜찮을 수도 있습니다. 하지만 데코레이터는 그런 경우가 아닙니다. 여기서 널리 받아들여지는 견해는 2.2와 2.3에서 사용된 후위 표기법의 문제를 피하기 위해 데코레이터를 문법적 기능으로 추가해야 한다는 것입니다. 데코레이터는 중요한 새 언어 기능이 될 예정이며, 그 설계는 2.3에서 구현할 수 있는 것에 제약받지 않고 미래를 내다보는 것이어야 합니다.
  • new keyword (and block)

    이 아이디어는 comp.lang.python에서 합의된 대안이었습니다(이에 대해서는 아래 Community Consensus에서 더 다룹니다.). Robert Brewer는 이 형식을 지지하는 인자를 정리한 상세한 J2 proposal 문서를 작성했습니다. 이 형태에 대해 처음 제기된 문제점은 다음과 같습니다:

    • 새로운 키워드가 필요하며, 따라서 from __future__ import decorators 문이 필요합니다.
    • 키워드 선택에 논란이 있습니다. 그러나 using이 합의된 선택으로 떠올랐으며, 제안과 구현에서 사용되고 있습니다.
    • 키워드/블록 형태는 일반 코드 블록처럼 보이지만 실제로는 그렇지 않은 것을 만들어 냅니다. 이 블록 안에서 문장을 사용하려고 하면 문법 오류가 발생하며, 이는 사용자를 혼란스럽게 할 수 있습니다.

    며칠 후 Guido는 두 가지 주요 근거를 들어 제안을 거부했는데(rejected the proposal), 첫째는 다음과 같습니다:

    … 들여쓰기된 블록이라는 문법 형태는 그 내용이 문(statement)의 나열이어야 한다는 것을 강하게 암시하지만, 실제로는 그렇지 않습니다. 표현식만 허용되며, 이 표현식들이 뒤따르는 함수 정의에 적용될 수 있을 때까지 암묵적으로 “수집”되는 과정이 일어납니다. …

    그리고 둘째는 다음과 같습니다:

    … 블록의 머리가 되는 줄을 시작하는 키워드는 많은 주의를 끕니다. 이는 “if”, “while”, “for”, “try”, “def”, “class”에 대해서도 마찬가지입니다. 하지만 “using” 키워드(또는 그 자리에 오는 다른 어떤 키워드)는 그런 주의를 받을 만하지 않습니다. 강조되어야 할 것은 스위트 안의 데코레이터 또는 데코레이터들인데, 이것들이 뒤따르는 함수 정의를 수식하는 중요한 요소이기 때문입니다. …

    독자 여러분께서는 the full response를 읽어 보시기 바랍니다.

  • 기타 형태

    the wiki page에 그 밖의 다양한 변형과 제안이 많이 있습니다.

왜 @입니까?

Java에서는 처음에 @를 Javadoc comments의 표식으로 사용했고, 이후 Java 1.5에서는 Python 데코레이터와 비슷한 annotations에 사용한 역사가 있습니다. 또한 @가 이전까지 Python에서 토큰으로 사용되지 않았으므로, 이러한 코드가 이전 버전의 Python에서 파싱되어 미묘한 의미상 버그로 이어질 가능성이 없다는 점이 분명합니다. 또한 무엇이 데코레이터이고 무엇이 아닌지에 대한 모호함도 사라집니다. 그렇긴 하지만 @는 여전히 상당히 임의적인 선택입니다. 대신 |를 사용하자고 제안한 사람들도 있었습니다.

데코레이터를 지정하는 데 (위치와 관계없이) 리스트 같은 문법을 사용하는 문법 선택지에 대해서는 [|...|], *[...]*, <...> 등 몇 가지 대안이 제안되었습니다.

현재 구현과 역사

Guido가 자신이 선호하는 문법을 구현할 지원자를 찾자, Mark Russell이 나서서 SF에 patch를 올렸습니다. 이 새 문법은 2.4a2에서 사용할 수 있었습니다.

@dec2
@dec1
def func(arg1, arg2, ...):
    pass

이는 다음과 동등합니다.:

def func(arg1, arg2, ...):
    pass
func = dec2(dec1(func))

다만 func라는 이름의 변수를 중간에 생성하지는 않습니다.

2.4a2에 구현된 버전은 한 줄에 여러 개의 @decorator 절을 허용했습니다. 2.4a3에서는 이를 한 줄에 데코레이터 하나만 허용하도록 강화했습니다.

list-after-def 문법을 구현한 Michael Hudson의 previous patch도 여전히 남아 있습니다.

2.4a2가 릴리스된 후 커뮤니티의 반응에 대응하여, Guido는 커뮤니티가 합의와 괜찮은 제안, 그리고 구현을 마련한다면 커뮤니티 제안을 재검토하겠다고 밝혔습니다. 엄청난 수의 게시물이 오가고 Python wiki에 방대한 수의 대안이 수집된 끝에 커뮤니티 합의가 도출되었습니다(아래 참조). Guido는 이후 이 대안 형식을 거부했지만(subsequently rejected), 다음과 같이 덧붙였습니다.

Python 2.4a3(이번 목요일 릴리스 예정)에서는 모든 것이 현재 CVS에 있는 그대로 유지됩니다. @는 Java의 비슷한 기능에서 사용하는 것과 같은 문자라는 장점이 있다고 생각하지만, 2.4b1에서는 @를 다른 단일 문자로 바꾸는 것을 고려하겠습니다. Java에서 @는 의미를 바꾸지 않는 속성에 사용되므로 완전히 같지는 않다는 주장도 있었습니다. 하지만 Python의 동적인 특성 때문에 Python의 구문 요소는 다른 언어의 비슷한 구성 요소와 결코 완전히 같은 의미를 갖지 않으며, 분명히 상당 부분이 겹칩니다. 서드파티 도구에 미치는 영향에 관해서는, IPython의 작성자는 영향이 크지 않을 것으로 생각하고, Leo의 작성자는 Leo가 살아남을 것이라고 말했습니다(다만 본인과 사용자들이 전환 과정에서 어느 정도 고통을 겪을 것이라고 했습니다). 사실 저는 Python 문법의 다른 곳에서 이미 쓰이는 문자를 고르면 그 경우 구문 분석이 더 미묘해져야 하므로, 외부 도구가 적응하기가 오히려 더 어려울 수 있다고 예상합니다. 하지만 솔직히 아직 결정하지 못했으므로 여기에는 어느 정도 조정의 여지가 있습니다. 지금 시점에서는 더 이상의 구문 대안을 검토하고 싶지 않습니다. 언젠가는 결론을 내려야 하고, 모두가 할 말을 했으며, 일은 계속 진행되어야 합니다.

커뮤니티 합의

이 절은 거부된 J2 문법을 기록하며, 역사적 완전성을 위해 포함되었습니다.

comp.lang.python에서 형성된 합의는 제안된 J2 문법이었습니다(“J2”는 PythonDecorators 위키 페이지에서 이 문법을 가리키던 이름입니다). 이는 def 문 앞에 새 키워드 using을 붙인 데코레이터 블록을 두는 방식입니다. 예를 들면 다음과 같습니다.:

using:
    classmethod
    synchronized(lock)
def func(cls):
    pass

이 문법을 지지하는 주요 인자는 “가독성은 중요하다(readability counts)”라는 원칙에 해당합니다. 간단히 말하면 다음과 같습니다.

  • 여러 개의 @줄보다 하나의 스위트가 더 낫습니다. using 키워드와 블록은 단일 블록인 def 문을 try/finally 등과 유사한 다중 블록 복합 구문으로 바꿉니다.
  • 새 토큰으로는 구두점보다 키워드가 더 낫습니다. 키워드는 기존의 토큰 사용 방식과 일치합니다. 새로운 토큰 범주가 필요하지 않습니다. 키워드를 사용하면 Python 데코레이터를 Java 어노테이션 및 .Net 속성과 구별할 수 있는데, 이들은 상당히 다른 존재입니다.

Robert Brewer가 이 형식에 대한 detailed proposal을 작성했고, Michael Sparks가 a patch를 만들었습니다.

앞서 언급했듯이 Guido는 이 형식을 거부했으며, python-dev와 comp.lang.python에 보낸 a message에서 이 형식의 문제점을 설명했습니다.

예시

comp.lang.python과 python-dev 메일링 리스트에서 이루어진 논의의 상당 부분은 staticmethod()와 classmethod() 내장 함수를 더 깔끔하게 사용하는 방법으로서 데코레이터를 사용하는 데 초점을 맞추고 있습니다. 이 기능은 그보다 훨씬 더 강력합니다. 이 절에서는 몇 가지 사용 예제를 제시합니다.

  1. 종료 시 실행될 함수를 정의합니다. 이 함수는 일반적인 의미에서 실제로 “래핑”되는 것이 아니라는 점에 유의하십시오.
    def onexit(f):
        import atexit
        atexit.register(f)
        return f
    
    @onexit
    def func():
        ...
    

    이 예제는 실제 사용에는 적합하지 않을 가능성이 높으며, 예시 목적으로만 제공된다는 점에 유의하십시오.

  2. 싱글턴 인스턴스를 갖는 클래스를 정의합니다. 클래스가 사라지고 나면, 진취적인 프로그래머라도 더 많은 인스턴스를 만들려면 더 창의적이어야 한다는 점에 유의하십시오. (python-dev에 올라온 Shane Hathaway의 글에서 가져왔습니다.)
    def singleton(cls):
        instances = {}
        def getinstance():
            if cls not in instances:
                instances[cls] = cls()
            return instances[cls]
        return getinstance
    
    @singleton
    class MyClass:
        ...
    
  3. 함수에 속성을 추가합니다. (python-dev에 Anders Munch가 올린 예제를 바탕으로 합니다.)
    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):
        ...
    
  4. 함수의 인자 타입과 반환 타입을 강제합니다. 이 코드는 func_name 속성을 이전 함수에서 새 함수로 복사한다는 점에 유의하십시오. func_name은 Python 2.4a3에서 쓰기 가능하게 되었습니다.:
    def accepts(*types):
        def check_accepts(f):
            assert len(types) == f.func_code.co_argcount
            def new_f(*args, **kwds):
                for (a, t) in zip(args, types):
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a,t)
                return f(*args, **kwds)
            new_f.func_name = f.func_name
            return new_f
        return check_accepts
    
    def returns(rtype):
        def check_returns(f):
            def new_f(*args, **kwds):
                result = f(*args, **kwds)
                assert isinstance(result, rtype), \
                       "return value %r does not match %s" % (result,rtype)
                return result
            new_f.func_name = f.func_name
            return new_f
        return check_returns
    
    @accepts(int, (int,float))
    @returns((int,float))
    def func(arg1, arg2):
        return arg1 * arg2
    
  5. 클래스가 특정 인터페이스(또는 인터페이스 집합)를 구현한다고 선언합니다. 이 예제는 PyProtocols를 사용한 경험을 바탕으로 Bob Ippolito가 python-dev에 올린 글에서 가져왔습니다.
    def provides(*interfaces):
         """
         An actual, working, implementation of provides for
         the current implementation of PyProtocols.  Not
         particularly important for the PEP text.
         """
         def provides(typ):
             declareImplementation(typ, instancesProvide=interfaces)
             return typ
         return provides
    
    class IBar(Interface):
         """Declare something about IBar here"""
    
    @provides(IBar)
    class Foo(object):
            """Implement something here..."""
    

물론 이 모든 예제는 문법적 지원이 없을 뿐, 현재도 구현할 수 있습니다.

(이제는 해결된) 미해결 문제

  1. 클래스 데코레이터가 향후 언어에 포함될지는 아직 확실하지 않습니다. Guido는 이 개념에 회의적인 입장을 표했지만, 여러 사람이 python-dev에서 이를 지지하는 strong arguments를 제시했습니다(PEP 318 -- posting draft로 검색하십시오). 클래스 데코레이터가 Python 2.4에 포함될 가능성은 극히 낮습니다.

    PEP 3129는 Python 2.6부터 클래스 데코레이터를 추가할 것을 제안합니다.

  2. @ 문자의 선택은 Python 2.4b1 이전에 재검토될 예정입니다.

    결국 @ 문자가 유지되었습니다.