설명자 마법 없이 Python 속성 조회를 하시겠습니까?
-
06-07-2019 - |
문제
나는 내가 작성한 코드에서 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
일반적인 객체 (예 : 클래스가 아님)입니다.