문제

나는 내가 작성한 코드에서 Python 설명자 프로토콜을 더 광범위하게 사용하기 시작했습니다.일반적으로 기본 Python 조회 마법이 내가 원하는 것이지만 때로는 설명자 개체의 결과 대신 설명자 개체 자체를 가져오고 싶을 때도 있습니다. __get__ 방법.설명자의 유형이나 설명자에 저장된 액세스 상태 등을 알고 싶습니다.

나는 올바른 순서라고 생각되는 네임스페이스를 탐색하고 설명자인지 여부에 관계없이 원시 속성을 반환하기 위해 아래 코드를 작성했습니다.표준 라이브러리에서 이 작업을 수행하기 위한 내장 함수나 무언가를 찾을 수 없다는 사실에 놀랐습니다. 그것이 거기에 있어야 한다고 생각하는데, 단지 그것을 눈치채지 못했거나 올바른 검색어를 검색하지 못했을 뿐입니다.

Python 배포판 어딘가에 이미 이 작업(또는 유사한 작업)을 수행하는 기능이 있습니까?

감사해요!

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

2009년 10월 28일 수요일 편집.

Denis의 답변은 설명자 클래스에서 설명자 개체 자체를 가져오는 데 사용할 수 있는 규칙을 제공했습니다.그러나 설명자 클래스의 전체 클래스 계층 구조가 있었고 시작하고 싶지 않았습니다. 모든 __get__ 상용구를 사용한 함수

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

이를 방지하기 위해 설명자 클래스 트리의 루트가 다음에서 상속되도록 만들었습니다.

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)
도움이 되었습니까?

해결책

대부분의 설명자는 인스턴스 속성으로 만 액세스 할 때 작업을 수행합니다. 따라서 수업에 액세스 할 때 스스로 반환하는 것이 편리합니다.

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>

디스크립터에 액세스하는 것은 서브 클래스로 범위를 세워야 할 때 유용 할 수 있지만 더 좋은 방법 이것을하기 위해.

다른 팁

그만큼 inspect 라이브러리는 설명자 마술없이 속성을 검색하는 기능을 제공합니다. inspect.getattr_static.

선적 서류 비치: https://docs.python.org/3/library/inspect.html#fetching-attributes-Stically

(이것은 오래된 질문이지만,이 작업을 수행하는 방법을 기억하려고 할 때 계속해서 만나고 있습니다. 그래서이 답을 게시하여 다시 찾을 수 있습니다!)

위의 방법

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>

컨테이너 개체 클래스에서 액세스하는 경우 딕셔너리, '설명자 매직'이 우회됩니다.또한 속성을 새 클래스 변수에 할당하면 '설명자 매직'을 사용하여 원본과 동일하게 동작하지만, 인스턴스 변수에 할당하면 속성은 일반 객체처럼 동작하고 '설명자 매직'도 우회합니다.

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

설명자를 얻고 싶다고 가정 해 봅시다. obj.prop 어디 type(obj) is C.

C.prop 일반적으로 설명자는 일반적으로 액세스 할 때 자체적으로 반환되기 때문에 작동합니다. C (즉, 바운드 C). 하지만 C.prop 메타 클라스에서 설명자를 트리거 할 수 있습니다. 만약에 prop 존재하지 않았습니다 obj, obj.prop 올라갈 것입니다 AttributeError 동안 C.prop 그렇지 않을 수 있습니다. 따라서 사용하는 것이 좋습니다 inspect.getattr_static(obj, 'prop').

당신이 그것에 만족하지 않는다면, 여기에 cpython 관련 방법이 있습니다 ( _PyObject_GenericGetAttrWithDict 안에 Objects/object.c):

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') cpython이 사용할 때와 같은 방식으로 디스크립터를 반환합니다. obj.prop 만약에 obj 일반적인 객체 (예 : 클래스가 아님)입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top