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 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

경고경고경고

이 문서는 데코레이터 구문과 그 결과로 결정이 내려진 과정을 설명하기 위한 것입니다. 잠재적인 대체 구문의 방대한 수를 다루려고 하지 않으며, 각 형식의 모든 장점과 단점을 철저히 나열하려고 하지도 않습니다.

개요

함수와 메서드를 변환하는 현재 방법(예를 들어, 이를 클래스 메서드나 정적 메서드로 선언하는 방법)은 불편하며 이해하기 어려운 코드로 이어질 수 있습니다. 이상적으로는 이러한 변환을 선언 자체가 이루어지는 코드상의 동일한 지점에서 수행해야 합니다. 이 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()는)는 Python 2.2부터 Python에서 사용할 수 있었습니다. 대략 그 시점부터 언젠가는 해당 데코레이터를 위한 어떤 구문 지원이 언어에 추가될 것이라고 가정해 왔습니다. 이러한 가정을 고려하면 합의에 도달하기가 왜 그렇게 어려웠는지 의문이 들 수 있습니다. 함수 데코레이터를 가장 잘 구현하는 방법을 두고 comp.lang.python과 python-dev 메일링 리스트 양쪽에서 때때로 논의가 격렬하게 벌어졌습니다. 이것이 그래야 하는 명확한 단 하나의 이유는 없지만, 몇 가지 문제가 가장 큰 분열을 일으키는 것으로 보입니다.

  • “의도 선언”을 어디에 배치해야 하는지에 대한 의견 불일치입니다. 거의 모든 사람이 함수 정의가 끝난 시점에 함수를 데코레이션하거나 변환하는 것은 최적이 아니라는 데 동의합니다. 그 외에는 이 정보를 어디에 배치해야 하는지에 대한 명확한 합의가 없는 것으로 보입니다.
  • 구문 제약 사항입니다. Python은 구문적으로 단순한 언어이지만, “엉망으로 만들지 않고” 할 수 있는 일과 할 수 없는 일에 상당히 강한 제약이 있습니다(시각적인 측면과 언어 파서의 관점 모두에서 그렇습니다). 이 개념을 처음 접하는 사람들이 “아, 무슨 일을 하는지 알겠어.”라고 생각하도록 이 정보를 구성할 뚜렷한 방법은 없습니다. 가능한 최선은 새로운 사용자가 구문의 의미에 대해 터무니없이 잘못된 정신 모델을 만들지 않도록 하는 것입니다.
  • 개념 전반에 대한 낯섦입니다. 대수(또는 기초 산술)를 조금이라도 알고 있거나 다른 프로그래밍 언어를 하나 이상 사용해 본 사람들에게 Python의 많은 부분은 직관적입니다. Python에서 데코레이터 개념을 접하기 전부터 이 개념을 경험해 본 사람은 매우 드뭅니다. 이 개념을 포착하는 강력한 기존 밈이 전혀 없습니다.
  • 일반적으로 구문에 관한 논의는 거의 어떤 다른 주제보다도 더 많은 논쟁을 일으키는 것으로 보입니다. 이에 대한 또 다른 예로 PEP 308과 관련하여 이루어진 삼항 연산자 논의를 참고하시기 바랍니다.

배경

현재 상황에 비해 구문 지원이 바람직하다는 데에는 일반적인 합의가 있습니다. Guido는 syntactic support for decorators10th Python Conference에서 진행한 DevDay 기조 발표에서 언급했지만, he later said 그것은 그가 그 자리에서 “반쯤 농담으로” 제안한 여러 확장 중 하나일 뿐이라고 말했습니다. Michael Hudson raised the topic 은 컨퍼런스 직후 python-dev 에서 이 주제를 제기했으며, 초기의 괄호 구문이 comp.lang.python 에서 Gareth McCaughan이 제안했던 이전 제안에서 비롯되었다고 설명했습니다.

클래스 정의와 함수 정의는 구문적으로 유사하므로 클래스 데코레이션은 다음 단계로서 명백해 보이지만, Guido는 여전히 확신하지 못하고 있으며 클래스 데코레이터는 Python 2.4에 포함되지 않을 것이 거의 확실합니다.

이 논의는 2002년 2월부터 2004년 7월까지 python-dev에서 간헐적으로 계속되었습니다. 수백 건도 넘는 게시물이 작성되었고, 사람들은 가능한 많은 구문 변형을 제안했습니다. Guido는 제안 목록을 EuroPython 2004로 가져갔고, 그곳에서 논의가 이루어졌습니다. 그 후 그는 Java-style @데코레이터 구문을 사용하기로 결정했으며, 이것은 2.4a2에서 처음 등장했습니다. Barry Warsaw는 데코레이터 구문과 거의 같은 시기에 열렸던 Pie-thon Parrot 슛아웃을 기념하고, @ 기호가 파이와 조금 비슷하게 보이기 때문에 이를 ‘pie-decorator’ 구문이라고 명명했습니다. Guido는 Python-dev에서 자신의 주장을 outlined his case 했으며, 그중 일부(많은 거부된 형식)에 관한 this piece 도 포함했습니다.

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

이 기능에 ‘데코레이터’라는 이름을 선택한 것에 대해 여러 불만이 제기되었습니다. 가장 큰 불만은 그 이름이 GoF book에서 사용된 방식과 일치하지 않는다는 것입니다. ‘데코레이터’라는 이름은 컴파일러 분야에서의 용례에서 비롯되었을 가능성이 큽니다 – 구문 트리를 순회하면서 주석을 추가합니다. 더 나은 이름이 나타날 가능성도 충분히 있습니다.

설계 목표

새 구문은 다음을 충족해야 합니다.

  • 사용자가 정의한 호출 가능 객체와 기존 내장 함수 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

이 형식에는 몇 가지 이의가 있습니다. 첫 번째 이의는 소스의 ‘greppability’를 쉽게 훼손한다는 점입니다. 이제는 ‘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
    

    이는 @데코레이터 구문의 한 변형입니다 – 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라는 결정을 내렸으며, 첫째 근거는 다음과 같습니다.

    … 들여쓰기된 블록의 구문 형식은 그 내용이 문들의 연속이어야 한다는 점을 강하게 시사하지만, 실제로는 그렇지 않습니다. 표현식만 허용되며, 이러한 표현식을 뒤따르는 함수 정의에 적용할 수 있을 때까지 암묵적으로 수집하는 작업이 진행됩니다. …

    그리고 둘째 근거는 다음과 같습니다.

    … 블록을 시작하는 행의 키워드는 많은 주의를 끕니다. 이는 “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에서는 이를 한 줄에 데코레이터 하나만 허용하도록 엄격하게 변경했습니다.

목록을 def 뒤에 배치하는 구문을 구현한 Michael Hudson의 previous patch도 여전히 남아 있습니다.

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

Python 2.4a3(이번 목요일에 출시될 예정)에서는 모든 것이 현재 CVS에 있는 상태로 유지됩니다. 2.4b1에서는 @가 Java의 유사한 기능에서 사용되는 것과 같은 문자라는 장점이 있다고 생각하지만, @를 다른 단일 문자로 변경하는 것을 고려하겠습니다. Java에서 @는 의미를 변경하지 않는 속성에 사용되므로 완전히 같은 것은 아니라는 주장이 제기되었습니다. 그러나 Python의 동적인 특성으로 인해 Python의 구문 요소는 다른 언어의 유사한 구성 요소와 결코 완전히 같은 의미를 갖지 않으며, 분명히 상당한 중복 영역이 존재합니다. 서드파티 도구에 미칠 영향과 관련하여, IPython의 작성자는 영향이 크지 않을 것이라고 생각하며, Leo의 작성자는 Leo가 살아남을 것이라고 말했습니다(다만 자신과 사용자들에게 과도기적인 불편을 초래할 것입니다). 실제로 Python 구문에서 이미 다른 곳에 사용되고 있는 문자를 선택하면 외부 도구가 이에 적응하기가 더 어려울 수 있다고 예상합니다. 그러한 경우에는 구문 분석이 더 정교해야 하기 때문입니다. 하지만 솔직히 아직 결정하지 못했으므로, 여기에는 어느 정도 조정의 여지가 있습니다. 이 시점에서 더 이상의 구문 대안을 고려하고 싶지는 않습니다. 언젠가는 결정을 내려야 하고, 모두가 자신의 의견을 밝혔으며, 이제는 일을 계속 진행해야 하기 때문입니다.

커뮤니티 합의

이 절에서는 거부된 J2 구문을 문서화하며, 역사적 완전성을 위해 포함합니다.

comp.lang.python에서 도출된 합의는 제안된 J2 구문이었습니다(“J2”는 PythonDecorators 위키 페이지에서 이를 지칭하던 방식입니다). 즉, 새로운 키워드 using을 데코레이터 블록 앞에 붙이고 def문 앞에 배치하는 방식입니다. 예를 들면 다음과 같습니다.:

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

이 구문을 지지하는 주요 주장은 “가독성이 중요하다”는 원칙에 해당합니다. 간단히 말하면 다음과 같습니다:

  • 여러 개의 @줄보다는 하나의 스위트가 더 낫습니다. using 키워드와 블록은 단일 블록 def 문을 try/finally 등과 유사한 다중 블록 복합 구성체로 변환합니다.
  • 새 토큰에는 구두점보다 키워드가 더 낫습니다. 키워드는 기존 토큰 사용 방식과 일치합니다. 새로운 토큰 범주는 필요하지 않습니다. 키워드는 파이썬 데코레이터를 자바의 애너테이션 및 .Net의 속성과 구별해 주는데, 이들은 상당히 다른 종류의 것입니다.

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

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

예제

comp.lang.pythonpython-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에서 이를 옹호하며 몇 가지 강력한 주장을 (PEP 318 -- posting draft를 검색해 보십시오) 펼쳤습니다. 클래스 데코레이터가 파이썬 2.4에 포함될 가능성은 매우 낮습니다.

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

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

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