2013-03-22 2 views
5

설명자/데코레이터를 중첩하려고 할 때 어떤 일이 발생하는지 이해하는 데 어려움을 겪고 있습니다. 파이썬 2.7 사용하고 있습니다.파이썬에서 중첩 디스크립터/데코레이터

예를 들어, propertyclassmethod 다음과 같은 단순화 된 버전을 보자 :

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print 'IN MyProperty.__get__' 
     return self.fget(obj) 

class MyClassMethod(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN MyClassMethod.__get__' 
     def f(*args, **kwargs): 
      return self.f(objtype, *args, **kwargs) 
     return f 

이 둥지를 시도 :

class A(object): 
    # doesn't work: 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 
    # works: 
    @MyProperty 
    def prop(self): 
     return 111 
    # works: 
    @MyClassMethod 
    def klsmethod(cls, x): 
     return x**2 

% print A.klsproperty 
IN MyProperty.__get__ 
... 
TypeError: 'MyClassMethod' object is not callable 

내부 설명 MyClassMethod__get__ 메소드가 호출 받고 있지 않습니다.

class B(object): 
    # works: 
    @NoopDescriptor 
    @MyProperty 
    def prop1(self): 
     return 888 
    # doesn't work: 
    @MyProperty 
    @NoopDescriptor 
    def prop2(self): 
     return 999 

% print B().prop1 
IN NoopDescriptor.__get__ 
IN MyProperty.__get__ 
888 
% print B().prop2 
IN MyProperty.__get__ 
... 
TypeError: 'NoopDescriptor' object is not callable 

: 중첩의 무 조작 기술자/장식을 사용하려고

class NoopDescriptor(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN NoopDescriptor.__get__' 
     return self.f.__get__(obj, objtype=objtype) 

: 나는 (내가 무슨 생각) 무 조작 기술자를 던지려고하는 이유 는 파악하지 못하면 왜 B().prop1이 작동하고 B().prop2을 이해하지 못합니다.

질문 : 내가 잘못 뭐하는 거지

  1. ? object is not callable 오류가 발생하는 이유는 무엇입니까?
  2. 올바른 방법은 무엇입니까? 예 : 데코레이터는 장식이 그것로 꾸미는 기능 호출 매개 변수없이 사용될 때 MyClassMethodMyProperty (또는 classmethodproperty)이 경우
+1

직관적으로 이해하는 열쇠는'@ MyProperty'가 메소드가 아닌 _data_ 속성처럼 작동하는 것을 생성하므로 메소드 설명자로 중첩 할 수 없습니다. 그 직관적 인 이해는 당신을 지금까지만 도달시킵니다. 전체 기사는 isedev의 답변에 있습니다. – abarnert

답변

3

당신은 당신의 코드가 작동 할 수 있습니다 :

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print('IN MyProperty.__get__') 
     try: 
      return self.fget.__get__(obj, objtype)() 
     except AttributeError: # self.fget has no __get__ method 
      return self.fget(obj) 

은 이제 예제 코드가 작동합니다

class A(object): 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 

print(A.klsproperty) 

을 출력은 다음과 같습니다

IN MyProperty.__get__ 
IN MyClassMethod.__get__ 
555 
+0

감사합니다. 그러나 이것이 권장 접근 방법입니까? 그렇다면, 내장 된'property'와'classmethod'는 어떻게 이런 식으로 행동하지 않고 (중첩 될 수 없습니까)? – shx2

+0

이것이 권장되지 않거나 내장 된'property'가 이것을 아직하지 못하는 이유가 확실하지 않습니다. 이 문제가 해결되지 않는 몇 가지 한계가 있다는 것을 알고 있습니다. 우선, 당신이 "MyClassMethod"를 더 영리하게 만들었다 고해도, 디스크립터를 역순으로 중첩시킬 수는 없다. 그리고'__set__' 서술자 함수가 클래스 변수 할당을 위해 호출되지 않는다고 생각합니다. 그래서 당신은 쓰기 가능한 클래스 속성이 작동하도록 메타 클래스 매직을 할 필요가 있습니다.아마도 더 많은 설명자 프로그래밍 경험을 가진 누군가가 다른 문제와 한계에 무게를 둘 수 있습니다. – Blckknght

5

를 다시 사용할 때 무엇이 ​​MyClassProperty을 정의하는 가장 좋은 방법 매개 변수. Decorator의 반환 값은 데코 레이팅 된 함수 대신 사용됩니다. 그래서 : MyProperty 이후

def prop(self): 
    ... 
prop = MyProperty(prop) 

실제로 A.prop.__get__()를 호출합니다 A.prop에 액세스 기술자 프로토콜을 구현하고,이에 (장식 된 개체를 호출하는 __get__을 정의했습니다에

@MyProperty 
def prop(self): 
    ... 

은 동일합니다 경우, 원래 함수/메서드), 그래서 모든게 잘 작동합니다.

이제 중첩시 :

@MyProperty 
@MyClassMethod 
def prop(self): 
    ... 

등가이다 :

def prop(self): 
    ... 
prop = MyClassMethod(prop) # prop is now instance of MyClassMethod 
prop = MyProperty(prop)  # prop is now instance of MyProperty 
          # (with fget == MyClassMethod instance) 

이제 전처럼 A.prop 액세스하는 사실 (MyProperty에서) A.prop.__get__()를 호출 한 후 호출하려고MyClassMethod의 인스턴스 (fget 속성에 데코 레이팅되고 저장된 객체).

그러나 MyClassMethod에는 __call__ 메서드가 정의되어 있지 않으므로 오류 MyClassMethod is not callable이 표시됩니다.


그리고 두 번째 질문을 해결하기 위해이 : 속성은 이미 클래스의 속성입니다 - 귀하의 예제에서, 클래스 객체의 속성 값을 반환합니다 A.prop에 액세스하고 A().prop는에서 속성의 값을 반환합니다 인스턴스 객체 (인스턴스가 인스턴스를 오버라이드하지 않은 경우 클래스 객체와 동일 할 수 있음) 당신은 포장 객체로 설명 프로토콜을 적용 MyProperty을 할 경우

+0

당신의 설명은 분명하다. 고맙습니다. 두 번째 대답에 관해서 : 나는 당신이 의미하는 바를 잘 모르겠다. 나는'classmethod'에 평행 한 것을 찾고 있는데, 이는 A.prop 또는 A(). prop'라고 부를 수 있고, 두 경우 모두 기본 함수에'cls' arg가 전달된다는 것을 의미합니다. – shx2

+0

이 경우,'obj' 인수의 타입을 검사 할 수 있습니다 : 클래스 인스턴스 인 경우 인스턴스 객체 대신 클래스를 추출하여 데코 레이팅 된 함수에 전달할 수 있습니다. – isedev

+0

당신이 요점을 놓치고있다 ... 내 질문은'classproperty' 데코레이터를 구현하는 방법에 관한 것이 아니다. 그것은 중첩 디스크립터에 관한 것이다. 당신이 제안하는 논리는'classmethod'에서 이미 구현되어 있습니다. 나는 논리를 다시 구현하는 대신에 그것을 성취하기 위해'property'와'classmethod'를 재사용/결합하는 깨끗한 방법이 있어야한다고 생각합니다. 그러나 나는 거기에 없다고 생각하기 시작하고있다. – shx2

0

Graham Dumpl에서 내 자신의 오래된 질문에 대한 확실한 답을 발견했습니다. 에톤의 매혹적인 blog. 한마디로

, 자신의 __get__()를 호출하여 내가 직접 랩 기능/개체를 호출을 시도함으로써 설명 프로토콜을 존중하는 대신 먼저 그들에게 그들의 "설명 마법"을 수행 할 수있는 기회를 제공하지 않습니다 쓴 장식 (먼저).

관련 문제