没有任何描述符魔法的python属性查找?

时间:2009-10-27 00:38:27

标签: python python-datamodel descriptor

我已经开始在我编写的代码中更广泛地使用python描述符协议。通常,默认的python查找魔术是我想要发生的,但有时我发现我想获取描述符对象本身而不是其__get__方法的结果。想要知道描述符的类型,或存储在描述符中的访问状态,或者某些事情。

我编写了下面的代码,按照我认为正确的顺序遍历名称空间,并返回属性raw,无论它是否是描述符。我很惊讶,虽然我无法在标准库中找到内置函数或其他内容来实现这一点 - 我认为它必须在那里,我只是没有注意到它或用Google搜索正确的搜索词。

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)

4 个答案:

答案 0 :(得分:12)

大多数描述符仅在作为实例属性访问时才能正常工作。因此,当为课程访问时返回自己很方便:

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来执行此操作。

答案 1 :(得分:3)

inspect库提供了一个函数来检索没有任何描述符魔法的属性:inspect.getattr_static

文档:https://docs.python.org/3/library/inspect.html#fetching-attributes-statically

(这是一个古老的问题,但我在试图记住如何做到这一点时仍然遇到它,所以我发布了这个答案,所以我可以再次找到它!)

答案 2 :(得分: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”的原始属性一样,但如果赋值给实例变量,则该属性的行为与任何普通对象相同,并且还会绕过'descriptor magic'。 / p>

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

答案 3 :(得分:0)

我们想要获取obj.prop type(obj) is C的描述符。

C.prop通常有效,因为描述符通常在通过C访问时返回自身(即绑定到C)。但C.prop可能会在其元类中触发描述符。如果prop中不存在obj,则obj.propAttributeError C.propinspect.getattr_static(obj, 'prop')可能不会_PyObject_GenericGetAttrWithDict。因此,最好使用Objects/object.c

如果您对此不满意,请参阅特定于CPython的方法(来自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')):

obj.prop
当CPython在obj使用它时,

new = ['JJ', 'NN', 'IN', 'NNP', 'NN', 'MD', 'VB', 'VBN', 'IN', 'NN', 'TO', 'VB', 'NN', 'CC', 'NN', 'TO', 'NNP', 'NN', 'NN', '.'] lst = [] for i,j in enumerate(new): lst1 = [] if j == 'IN': for i in new[i+1:]: if 'NN' in i: lst1.append(i) lst.append(lst1) break lst = [['NNP'], ['NN']] 以相同的方式返回描述符,如果[['NNP', 'NN'], ['NN'], ['NNP', 'NN', 'NN'] 是通常的对象(例如,不是类)。