2012-08-23 4 views
0

파이썬 프로퍼티가 생성 된 후에 게터를 변경할 수 있습니까?파이썬 : 사실 이후에 프로퍼티 게터 변경

아이디어는 일단 한번봤을 때 다시 보지 않아도됩니다 (사전은 변경되지 않음). 내가 할 수있는 :

class A: 
    _lookup_str = 'hi' 
    _thing = None 
    @property 
    def thing(): 
     if not value: 
      value = some_dictionary[_lookup_str] 
     return value 

그러나 심지어이 나는 조건 테스트 해요 - 내가 전부 게터를 제거하고 값을 대체 할 수있는 것보다 더 많은 작업이다.

+0

중복 http://stackoverflow.com/questions/815110/is-there-a-decorator- : 새로운 스타일의 클래스

, 당신은 __getattribute__

간단한 수정을 오버라이드 (override) 할 필요가 있습니다 to-simply-cache-function-return-values ​​ –

+0

Im 당신이 성취하려고하는 것이 무엇인지 완전히 모르겠다 ... 사전 조회는 상대적으로 빠르며 거의 일정 시간이며, 속성을 사용하면 미래 요구 사항이 안전하고 효율적으로 구현됩니다. 속성을 사용하기위한 약간의 오버 헤드가 있지만 컨투어 ... 무거운/중복 계산 결과 캐싱 완전히 다른 이야기. –

+0

@DmitryBeransky 중복이 아닙니다. 나는 self-replacement getter를 생성 할 데코레이터를 원한다. Tobias는 단지 게으른 로딩을하는 데코레이터를 원했습니다. getter는 자신이 계산/가져 오는 데이터로 대체하지 않습니다. –

답변

7

Werkzeug에는 정확히 원하는대로 수행하는 cached_property decorator이 있습니다. 첫 번째 함수 호출 후 함수에 대한 __dict__ 항목을 첫 번째 호출의 출력으로 바꿉니다. (? BTW - 코드 나 이런 일에 대한 코드에 링크를 게시하는 것이 좋습니다) : 여기

은 ( werkzeug.utils on github에서 길이에 따라 약간 씩 편집) 코드의

_missing = object() 

class cached_property(object): 
    """A decorator that converts a function into a lazy property. The 
    function wrapped is called the first time to retrieve the result 
    and then that calculated result is used the next time you access 
    the value:: 

     class Foo(object): 

      @cached_property 
      def foo(self): 
       # calculate something important here 
       return 42 

    The class has to have a `__dict__` in order for this property to 
    work. 
    """ 

    # implementation detail: this property is implemented as non-data 
    # descriptor. non-data descriptors are only invoked if there is 
    # no entry with the same name in the instance's __dict__. 
    # this allows us to completely get rid of the access function call 
    # overhead. If one choses to invoke __get__ by hand the property 
    # will still work as expected because the lookup logic is replicated 
    # in __get__ for manual invocation. 

    def __init__(self, func, name=None, doc=None): 
     self.__name__ = name or func.__name__ 
     self.__module__ = func.__module__ 
     self.__doc__ = doc or func.__doc__ 
     self.func = func 

    def __get__(self, obj, type=None): 
     if obj is None: 
      return self 
     value = obj.__dict__.get(self.__name__, _missing) 
     if value is _missing: 
      value = self.func(obj) 
      obj.__dict__[self.__name__] = value 
     return value 

당신이 만약 왜 이것이 작동하는지 더 자세히 알고 싶다면 Python docs on descriptors을 확인하십시오. 위의 코드는 덮어 쓸 수있는 비 데이터 디스크립터 (@property과 달리)를 만듭니다.

+0

여기에 대응하는 코드가 좋습니다. [FAQ] (http : // stackoverflow.com/questions/how-to-answer) : "잠재적 솔루션에 대한 링크는 언제나 환영하지만, 링크 주위에 컨텍스트를 추가하여 동료 사용자가 그것이 무엇인지, 왜 있는지에 대해 알 수 있도록하십시오. 대상 사이트에 도달 할 수 없거나 영구적으로 오프라인 상태가되는 경우 중요한 링크의 일부입니다. " – Dougal

+0

이 코드가 작동하기 전에'_missing = object()'와 같은 일을 할 필요가 있습니다. – Dougal

+0

@Dougal Werkzeug에서'_missing'은 특별한'__repr__'과'__reduce__'를 가진 객체의 서브 클래스 일뿐입니다. 나는 당신이 말한 것을 넣었습니다 ... 그것을 센티널 가치로 사용할 때 분명히 읽기 쉽습니다. –

0

Jeff Tratner가 제시 한 대답은 python 개체의 __dict__에있는 속성 개체를 덮어 쓰는 것입니다. Werkzeug의 cached_property가 나에게 너무 복잡해 보입니다. 다음 (훨씬 더 간단) 코드는 나를 위해 작동 :

def cached_property(f): 
    @property 
    def g(self, *args, **kwargs): 
     print 'trace' 
     value = f(self, *args, **kwargs) 
     self.__dict__[f.__name__] = value 
     return value 
    return g 

class A: 
    @cached_property 
    def thing(self): 
     return 5 

a = A() 
print a.thing 
print a.thing 
print a.thing 
print a.thing 

# 'trace' is only shown once -- the first time a.thing is accessed. 
+0

코드를 실행하려고하면 2.7 또는 3.2에서 작동하지 않았다; 매번 '추적'을 보여줍니다. 또한 설정 한 방식에 따라 속성을 덮어 쓸 설정자를 명시 적으로 정의해야합니다. –

+0

파이썬 2.7에서 실행하셨습니까? 파이썬 2.7.2에서'trace '를 한 번,'5'를 네 번 얻었다. 3.2에서 테스트하지는 않았지만 nehz는 Python 3에서 작동하지 않을 것이라고 동의합니다. –

+0

이전 스타일을 사용할지 아니면 새로운 스타일을 사용할지 (즉, 클래스 B를 추가하면 객체) :'그것은 다른 모든 방법으로 'A'와 동일합니다. 반복해서 추적 할 것입니다.) 문제는'property'가 데이터 디스크립터를 생성하기 때문에 setter 없이는 무시할 수없고'__dict__' 룩업을 무시한다는 것이다. '__get__' 메소드로 정의하면 잘 동작합니다. –

0

그것은 사용하지 않는 것이 좋습니다 있지만 이전 스타일의 클래스에 대한 작동합니다

>>> class A: 
... @property 
... def thing(self): 
...  print 'thing' 
...  self.thing = 42 
...  return self.thing 
... 
>>> a = A() 
>>> a.thing 
thing 
42 
>>> a.thing 
42 
>>> a.thing 
42 

그것은 새로운 스타일의 클래스에 대한 작동하지 않습니다 (서브 클래스 type, object의) 모든 클래스가 새로운 스타일 인 Python 3에서는 작동하지 않습니다. 이 경우 @Jeff Tratner's answer을 사용하십시오.

0

J.F. Sebastian과 Isaac Sutherland의 대답은 새로운 스타일 수업에서는 작동하지 않습니다. Jeff Tratner가 언급 한 결과가 생성됩니다. 모든 액세스마다 추적을 인쇄합니다.

def cached_property(f): 
    @property 
    def g(self, *args, **kwargs): 
     print 'trace' 
     value = f(self, *args, **kwargs) 
     self.__dict__[f.__name__] = value 

     return value 
    return g 

def cached_class(c): 
    def d(self, name): 
     getattr = object.__getattribute__ 
     if (name in getattr(self, '__dict__')): 
      return getattr(self, '__dict__')[name] 
     return getattr(self, name) 

    c.__getattribute__ = d 
    return c 

@cached_class 
class A(object): 
    @cached_property 
    def thing(self): 
     return 5 

a = A() 
print a.thing 
print a.thing 
print a.thing 
print a.thing