2012-04-15 5 views
3

클래스의 특정 메소드에 데코레이터를 구현하려고 시도 했으므로 값이 아직 계산되지 않은 경우 메소드가 값을 계산합니다. 그렇지 않으면 메소드가 미리 계산 된 값을 반환합니다. 이는 defaultdict 인스턴스에 저장됩니다. 클래스 외부에서 선언 된 데코레이터 내부에서 인스턴스 defaultdict에 액세스하는 방법을 알아낼 수 없습니다. 이것을 구현하는 방법에 대한 아이디어가 있습니까? 여기 클래스 외부에서 셀프 액세스하기

from collections import defaultdict 
from math import sqrt 

내 장식입니다 :

class CalcOrPass: 
    def __init__(self, func): 
     self.f = func 

    #if the value is already in the instance dict from SimpleData,   
    #don't recalculate the values, instead return the value from the dict 
    def __call__(self, *args, **kwargs): 

     # can't figure out how to access/pass dict_from_SimpleData to here :(
     res = dict_from_SimpleData[self.f.__name__] 
     if not res: 
      res = self.f(*args, **kwargs) 
      dict_from_SimpleData[self.f__name__] = res 
     return res 

그리고 여기에 장식 된 방법과 SimpleData 클래스의 :

class SimpleData: 
    def __init__(self, data): 
     self.data = data 
     self.stats = defaultdict() #here's the dict I'm trying to access 

    @CalcOrPass 
    def mean(self): 
     return sum(self.data)/float(len(self.data)) 

    @CalcOrPass 
    def se(self): 
     return [i - self.mean() for i in self.data] 

    @CalcOrPass 
    def variance(self): 
     return sum(i**2 for i in self.se())/float(len(self.data) - 1) 

    @CalcOrPass 
    def stdev(self): 
     return sqrt(self.variance()) 

여기

은 (작업 예) 수입입니다 지금까지 꾸미기를 선언하려고 시도했습니다 안에 SimpleData, 데코레이터로 여러 개의 인수를 전달하려했는데 (분명히이 작업을 수행 할 수 없음), 내 전갈 탱크에 종이 비행기를 던지면서 회전 의자에서 돌고 있습니다. 어떤 도움을 주시면 감사하겠습니다!

+0

질문에 대한 답변이 없지만'SimpleData' 인스턴스가 아닌'defaultdict'가 데코레이터에 존재하지 않아야합니까? 나는'SimpleData'가 데코레이터의 구현 세부 사항 (당신의 문제를 해결할 것입니다)에 대해 알아야하는 이유를 알지 못합니다. – icecrime

+0

그럴 수도 있습니다. 그러나 SimpleData의 모든 인스턴스에 대해 다른 기본 사전이 있어야합니다. –

+2

사이드 노트 : Python 2에서 코딩 할 때 항상'object'의 클래스를 상속합니다. 그렇게하지 않으면 "이전 스타일"클래스를 제공합니다. 예상했던 것과 다른 행동을합니다. – jsbueno

답변

4

데코레이터를 정의하는 방식에 따라 대상 오브젝트 정보가 손실됩니다. 대신 함수 래퍼를 사용

def CalcOrPass(func): 
    @wraps(func) 
    def result(self, *args, **kwargs): 
     res = self.stats[func.__name__] 
     if not res: 
      res = func(self, *args, **kwargs) 
      self.stats[func.__name__] = res 
     return res 
    return result 

wraps 여기에 반드시 필요하지만, 매우 편리 functools에서하고 있지.


사이드 참고 : defaultdict는 공장 함수 인수 취

defaultdict(lambda: None) 

을하지만 어쨌든 키의 존재 여부를 테스트하고 있기 때문에, 당신은 간단한 dict를 선호한다.

2

함수가 정의되어있을 때 언 바운드이기 때문에 원하는 것을 할 수 없습니다. 다음은 런타임에 일반적인 방식으로 그것을 달성하는 방법이다 :

class CalcOrPass(object): 
    def __init__(self, func): 
     self.f = func 

    def __get__(self, obj, type=None): # Cheat. 
     return self.__class__(self.f.__get__(obj, type)) 

    #if the value is already in the instance dict from SimpleData, 
    #don't recalculate the values, instead return the value from the dict 
    def __call__(self, *args, **kwargs): 
     # I'll concede that this doesn't look very pretty. 
     # TODO handle KeyError here 
     res = self.f.__self__.stats[self.f.__name__] 
     if not res: 
      res = self.f(*args, **kwargs) 
      self.f.__self__.stats[self.f__name__] = res 
     return res 

짧은 설명 :

  • 우리의 장식이 __get__을 정의 (및 그러므로 설명이라고합니다). 속성 액세스에 대한 기본 동작은 객체의 사전에서 가져 오는 것이지만, 설명자 메소드가 정의되어 있으면 파이썬이이를 대신 호출합니다. 객체와
  • 사건은 object.__getattribute__type(b).__dict__['x'].__get__(b, type(b))
  • 우리는 기술자의 매개 변수에서 바인딩 클래스와 그 유형에 액세스 할 수 있습니다이 방법에 b.x 같은 접근을 변환이다.
  • 그런 다음 이전의 언 바운드 함수 대신 바운드 메서드를 꾸미기 (래핑)하는 새 CalcOrPass 개체를 만듭니다.
  • 새 스타일 클래스 정의에 유의하십시오. 나는 이것을 시도하지 않았기 때문에 예전 스타일의 클래스에서 작동하는지 확신 할 수 없다. 그냥 사용하지 마십시오. :) 그러나 이것은 함수와 메소드 모두에서 작동 할 것입니다.
  • "오래된"데코 레이팅 된 기능은 어떤 연습으로 남아 있습니다.
+0

굉장 : 해결책과 좋은 설명에 감사드립니다. –

+0

파이썬 2.6 또는 그 이후 버전을 사용한다면, 클래스 데코레이터를 사용하고, 모든 클래스 속성을 거쳐 캐싱 함수를 바인드 할 수있다. (또는 다른 데코레이터에 의해 표시된 것들을 찾아서 바인딩 할 수도있다.) 그러나 그것은 엄청난 고통입니다. :-) – torek

+0

@torek : 캐싱 기능? 그게 그냥 메모 기능인가요? –

관련 문제