2009-10-27 7 views
6

필자가 작성한 코드에서 파이썬 설명자 프로토콜을 더 광범위하게 사용하기 시작했습니다. 일반적으로 기본 파이썬 조회 마술은 내가 원하는 일이지만 때로는 그 대신 __get__ 메서드의 결과 대신 설명자 개체 자체를 가져 오려고합니다. 설명자의 유형을 알고 싶거나 설명자에 저장된 상태에 액세스하고 싶거나 몇 가지 일이 있습니다.설명자 마술없이 파이썬 속성 조회?

올바른 순서가 있다고 믿는 네임 스페이스를 탐색하고 설명자인지 여부에 관계없이 원시 특성을 반환하도록 아래 코드를 작성했습니다. 나는 표준 라이브러리에서 내장 함수 나 무언가를 찾을 수는 없지만 놀랍다. 나는 거기에 있어야한다는 것을 알았고, 나는 그것을 알아 채지 못했거나 올바른 검색 용어로봤을 뿐이다.

이미 파이어 폭스 배포판에 이미 이와 비슷한 기능이 있습니까?

감사합니다.

from inspect import isdatadescriptor 

def namespaces(obj): 
    obj_dict = None 
    if hasattr(obj, '__dict__'): 
     obj_dict = object.__getattribute__(obj, '__dict__') 

    obj_class = type(obj) 
    return obj_dict, [t.__dict__ for t in obj_class.__mro__] 

def getattr_raw(obj, name): 
    # get an attribute in the same resolution order one would normally, 
    # but do not call __get__ on the attribute even if it has one 
    obj_dict, class_dicts = namespaces(obj) 

    # look for a data descriptor in class hierarchy; it takes priority over 
    # the obj's dict if it exists 
    for d in class_dicts: 
     if name in d and isdatadescriptor(d[name]): 
      return d[name] 

    # look for the attribute in the object's dictionary 
    if obj_dict and name in obj_dict: 
     return obj_dict[name] 

    # look for the attribute anywhere in the class hierarchy 
    for d in class_dicts: 
     if name in d: 
      return d[name] 

    raise AttributeError 

편집 10월 28일 (수),

데니스의 대답 2009 년은 나에게 기술자가 자신을 개체를 얻을 내 기술자 클래스에서 사용하는 규칙을했다. 그러나, 나는 기술자 클래스의 전체 클래스 계층 구조를 가지고 있었고, 내가

def __get__(self, instance, instance_type): 
    if instance is None: 
     return self 
    ... 

이러한 문제가 발생하지 않도록하려면 보일러와 마다__get__ 기능을 시작하고 싶지 않았다, 나는 기술자 클래스 트리의 루트에서 상속 만든 다음과 같습니다 :

def decorate_get(original_get): 
    def decorated_get(self, instance, instance_type): 
     if instance is None: 
      return self 
     return original_get(self, instance, instance_type) 
    return decorated_get 

class InstanceOnlyDescriptor(object): 
    """All __get__ functions are automatically wrapped with a decorator which 
    causes them to only be applied to instances. If __get__ is called on a 
    class, the decorator returns the descriptor itself, and the decorated 
    __get__ is not called. 
    """ 
    class __metaclass__(type): 
     def __new__(cls, name, bases, attrs): 
      if '__get__' in attrs: 
       attrs['__get__'] = decorate_get(attrs['__get__']) 
      return type.__new__(cls, name, bases, attrs) 
+0

때로는 설명자 개체를 원하십니까? 이것은 기술자에 대한 핵심 기대 사항을 위반합니다. 속성과 같아 져야합니다. 그 근본적인 기대를 깨뜨리는 이유는 무엇입니까? 왜 그럴까요? 왜 그렇게 복잡한 것을 만드 는가? –

+0

내가하고있는 일은 나에게 복잡해 보이지 않지만 디자인을 실험하고 있다고 말할 수있을 것 같다. 현재 나의 특별한 경우에는 게임에서 무기의 강도를 반환하는 설명자가 있습니다. 이 값은 기술자의 상태 (무기의 강도)와 인스턴스 (우주의 건강)의 함수입니다. 무기 종류는 다양합니다. 일반적으로 저는 값의 결과를 원하지만, 어떤 경우에는 어떤 종류의 무기인지, 즉 기술자의 유형을 알아야합니다. 설명자에 설명자 프로토콜의 일부가 아닌 메서드가 있고이를 호출하고 싶다면 어떻게해야합니까? –

답변

11

대부분의 설명자는 인스턴스 속성으로 만 액세스 할 때 작업을 수행합니다.

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

이것은 당신이 설명 자체를 얻을 수 있습니다 :

>>> class C(object): 
...  prop = FixedValueProperty('abc') 
... 
>>> o = C() 
>>> o.prop 
'abc' 
>>> C.prop 
<__main__.FixedValueProperty object at 0xb7eb290c> 
>>> C.prop.value 
'abc' 
>>> type(o).prop.value 
'abc' 

참고이 너무 (? 대부분의) 내장 디스크립터 작동하는지,이 클래스에 대한 액세스 때 그래서 그 자체를 반환 편리합니다 :

>>> class C(object): 
...  @property 
...  def prop(self): 
...   return 'abc' 
... 
>>> C.prop 
<property object at 0xb7eb0b6c> 
>>> C.prop.fget 
<function prop at 0xb7ea36f4> 

액세스 기술자는 서브 클래스의 범위를해야 할 때 유용 할 수 있지만,이 작업을 수행 할 수있는 better way이 있었다. 당신은 호텔의 코드를 제어 할 때마다

+0

깨닫지 못했습니다. 알아 둘만한. 함수 자체는 해당 패턴에 대한 예외가 될 수 있지만, 아마도 유일한 것입니다. 붙박이 기술자를 찌르십시오. –

+0

정확히 묻는대로 내 질문에 대답하지 않았지만 귀하의 대답은 제가 근본적인 문제를 해결하는 데 도움이되었습니다. 나는 그것을 받아 들일 것이다. –

+0

은이 패턴의 예외 함수입니다 (나는 당신이 메서드에 대해 말한다고 가정합니다)? 아니요,'c.method'는 언 바운드 메소드를 리턴하는 반면,'c.method'는 설명에서 바운드 메소드를 리턴합니다. 그것은 같은 패턴입니다. – u0b34a0f6ae

0

위의 방법

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

은 좋은 방법이지만, 어떤 경우는 속성이 다른 곳 다른 사람에 의해 제어 라이브러리의 일부입니다 때와 같이있다 접근법이 유용합니다. 이 대체 접근법은 객체 매핑 구현, 질문에 설명 된 네임 스페이스 걷기 또는 다른 특수 라이브러리와 같은 다른 상황에서도 유용 할 수 있습니다.

간단한 속성 클래스를 고려

class ClassWithProp: 

    @property 
    def value(self): 
     return 3 
>>>test=ClassWithProp() 
>>>test.value 
3 
>>>test.__class__.__dict__.['value'] 
<property object at 0x00000216A39D0778> 

클래스 DICT에서, '기술자 마법'이 바이 패스 컨테이너 객체에서 액세스합니다.새로운 클래스 변수에 속성을 할당하면 'descriptor magic'이있는 원본과 똑같이 동작하지만 인스턴스 변수에 할당 된 경우이 속성은 정상적인 개체로 작동하고 'descriptor magic'을 우회합니다. inspect.getattr_static :

>>> test.__class__.classvar = test.__class__.__dict__['value'] 
>>> test.classvar 
3 
>>> test.instvar = test.__class__.__dict__['value'] 
>>> test.instvar 
<property object at 0x00000216A39D0778> 
0

에 대한 설명자를 얻고 자하는 경우, type(obj) is C입니다.

C.prop은 일반적으로 C (즉, C에 바인딩 됨)을 통해 액세스 할 때 설명자가 대개 반환되기 때문에 정상적으로 작동합니다. 그러나 C.prop은 메타 클래스에서 설명자를 트리거 할 수 있습니다. propobj에 없으면 obj.propAttributeError을 발생시키고 C.prop은 그렇지 않을 수 있습니다. 따라서 inspect.getattr_static(obj, 'prop')을 사용하는 것이 좋습니다. obj이 인 경우 CPython과는 obj.prop에 그것을 사용하는 경우

import ctypes, _ctypes 

_PyType_Lookup = ctypes.pythonapi._PyType_Lookup 
_PyType_Lookup.argtypes = (ctypes.py_object, ctypes.py_object) 
_PyType_Lookup.restype = ctypes.c_void_p 

def type_lookup(ty, name): 
    """look for a name through the MRO of a type.""" 
    if not isinstance(ty, type): 
     raise TypeError('ty must be a type') 

    result = _PyType_Lookup(ty, name) 
    if result is None: 
     raise AttributeError(name) 

    return _ctypes.PyObj_FromPtr(result) 

type_lookup(type(obj), 'prop')이 같은 방법으로 기술자를 반환 : 당신이 만족하지 않는 경우

, 여기에 (_PyObject_GenericGetAttrWithDict에서 Objects/object.c 단위) CPython의 특정 방법입니다 일반적인 객체 (예 : 클래스가 아닙니다).