2015-01-29 2 views
2

파이썬의 디스크립터에 대한 몇 가지 사항을 명확히하고 싶습니다. 몇 가지 복잡한 set/mechanics를 사용하여 클래스에 속성을 추가하고 해당 계산 된 값을 디스크립터 객체에 캐시하려고합니다. 단순화 된 예는 다음과 같습니다파이썬 : 디스크립터의 캐시 값

class Pro(object): 
    """My descriptor class""" 

    def __init__(self): 
     self.value = None 

    def __get__(self, instance, owner): 
     return self.value 

    def __set__(self, instance, value): 
     self.value = value 


class Something(object): 
    """My simple class""" 
    pro = Pro() 

a = Something() 
a.pro = 1 

b = Something() 
b.pro = 2 

print(a.pro, b.pro) # At first, I've expected them to be 1 and 2 

나는 분명히 내가 잘못했다 pro 속성이 Something의 모든 인스턴스에 대한 Pro의 고유 한 인스턴스 될 것이라고 어떻게 든 생각했다. 마치 대신 과 __get__과 같이 instance._value을 사용해야하는 것처럼 보이지만 실제로는 Pro 클래스의 모든 것을 숨기를 바랬습니다. 이것은 가능한가? 감사!

+0

WeakKeyDictionary은 한 번 Something의 인스턴스가 참조가 다음 남아 있지 가비지 정상 dict 가능하지 않은 즉시 수집이 있는지 확인합니다 [this] (https://pypi.python.org/pypi/cached-property/0.1.5)를 보셨습니까? – Lemming

+0

@Lemming, 꽤 고맙게 여겨지지만, 고마워,하지만 난 표준 라이브러리로만 이것을 해결하기를 바랬다 :) –

+0

충분히 공정하게. Btw, 대부분의 파이썬 패키지는 오픈 소스입니다. [실제 코드] (https://github.com/pydanny/cached-property/blob/master/cached_property.py)는 상당히 작으며 비 스레딩 버전은 추가 패키지가 필요하지 않습니다. – Lemming

답변

3

코드의 문제점은 모든 인스턴스가 Something으로 공유 할 Pro 인스턴스에 속성을 설정한다는 것입니다.

class Meta(type): 
    def __new__(cls, name, bases, dct): 
     for k, v in dct.items(): 
      if isinstance(v, Pro): 
       # add an _ in front of the name 
       v.name = '_' + k 
     return super(Meta, cls).__new__(cls, name, bases, dct) 


class Pro(object): 

    def __get__(self, ins, typ): 
     return getattr(ins, self.name) 

    def __set__(self, ins, val): 
      setattr(ins, self.name, val) 

class Something(object): 
    """My simple class""" 
    __metaclass__ = Meta 
    pro = Pro() 

a = Something() 
a.pro = 1 

b = Something() 
b.pro = 2 

데모 : : 그래서

>>> a.pro, b.pro 
(1, 2) 
>>> a.__dict__ 
{'_pro': 1} 
>>> b.__dict__ 
{'_pro': 2} 
>>> a.pro = 100 
>>> a.__dict__ 
{'_pro': 100} 

당신이 Something의 개별 인스턴스의 속성을 설정해야이 문제를 해결하려면 그렇게하는 한 가지 방법은 메타 클래스를 사용하는 것입니다 무언가에 숨겨진 속성을 만드는 방법이 없습니다 인스턴스, 맞습니까?

아니요, 있습니다. 의 각 인스턴스와 관련된 모든 값을 저장하는 Pro의 인스턴스에 사전을 저장할 수 있습니다. 예를 들어, Something의 인스턴스가 해시 가능한 경우 weakref.WeakKeyDictionary을 사용하여 다음과 같이 할 수 있습니다.

from weakref import WeakKeyDictionary 

class Pro(object): 

    def __init__(self): 
     self.instances = WeakKeyDictionary() 

    def __get__(self, ins, typ): 
     return self.instances[ins] 

    def __set__(self, ins, val): 
     self.instances[ins] = val 

p = Pro() 

class Something(object): 
    """My simple class""" 
    pro = p 

a = Something() 
a.pro = 1 

b = Something() 
b.pro = 2 

print a.pro, b.pro 

print p.instances.items() 
del a 
print p.instances.items() 

출력 :

1 2 
[(<__main__.Something object at 0x7fb80d0d5310>, 1), (<__main__.Something object at 0x7fb80d0d5350>, 2)] 
[(<__main__.Something object at 0x7fb80d0d5350>, 2)] 
+0

그래서'Something' 인스턴스에 숨겨진 속성을 만드는 방법은 없습니다. –

+0

인스턴스의 값을 '캐시'하려면 해당 값이 인스턴스와 연관되어야합니다. 파이썬 속성은 기본적으로 객체의 __dict__ 사전을 사용하여이를 수행합니다. 인스턴스를 키로 사용하여 설명에 사전을 넣으면이를 되돌릴 수 있지만 기본 문제는 인스턴스와 값의 매핑이 필요하거나 그 반대의 경우입니다. –

+0

@AntonMelnikov 물론 업데이트도 가능합니다. –