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

python属性查找没有任何描述符魔法?,python,python-datamodel,descriptor,Python,Python Datamodel,Descriptor,在我编写的代码中,我已经开始更广泛地使用python描述符协议。通常,我希望使用默认的python查找魔法,但有时我发现我希望获取描述符对象本身,而不是它的\uuuuuu get\uuuu方法的结果。想要知道描述符的类型,或者描述符中存储的访问状态,等等 我编写了下面的代码,以便按照我认为正确的顺序遍历名称空间,并返回属性raw,而不管它是否是描述符。但我很惊讶,我在标准库中找不到内置函数或其他东西来实现这一点——我想它一定在那里,我只是没有注意到它,也没有在谷歌上搜索正确的搜索词 python

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

我编写了下面的代码,以便按照我认为正确的顺序遍历名称空间,并返回属性raw,而不管它是否是描述符。但我很惊讶,我在标准库中找不到内置函数或其他东西来实现这一点——我想它一定在那里,我只是没有注意到它,也没有在谷歌上搜索正确的搜索词

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的回答给了我一个约定,在描述符类中使用它来获取描述符对象本身。但是,我有一个完整的描述符类的类层次结构,我不想用一个样板来开始每个
\uu get\uu
函数

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'
>>C类(对象):
...     prop=FixedValueProperty(“abc”)
... 
>>>o=C()
>>>o.道具
“abc”
>>>C.支柱
>>>道具价值
“abc”
>>>类型(o).属性值
“abc”
请注意,这也适用于(大多数?)内置描述符:

>>> class C(object):
...     @property
...     def prop(self):
...         return 'abc'
... 
>>> C.prop
<property object at 0xb7eb0b6c>
>>> C.prop.fget
<function prop at 0xb7ea36f4>
>>C类(对象):
...     @财产
...     def道具(自身):
...         返回“abc”
... 
>>>C.支柱
>>>C.prop.fget
当您需要在子类中扩展描述符时,访问描述符可能很有用,但有一种方法可以做到这一点。

上述方法

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>
ClassWithProp类:
@财产
def值(自身):
返回3
>>>test=ClassWithProp()
>>>测试值
3.
>>>测试.__类_;.__;指令__;。['value']
从容器对象类dict访问时,将绕过“描述符魔术”。还要注意的是,如果我们将属性分配给一个新的类变量,它的行为就像原始的“描述符魔术”,但如果分配给一个实例变量,该属性的行为就像任何普通对象一样,也会绕过“描述符魔术”

>>> test.__class__.classvar =  test.__class__.__dict__['value']
>>> test.classvar
3
>>> test.instvar = test.__class__.__dict__['value']
>>> test.instvar
<property object at 0x00000216A39D0778>
>>test.\uuuuuu class\uuuuuu.classvar=test.\uuuuuu class\uuuuuuuuuu dict\uuuuu['value']
>>>test.classvar
3.
>>>test.instvar=test.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
>>>测试仪器

inspect库提供了一个无需任何描述符魔法即可检索属性的函数:
inspect.getattr\u static

文件:


(这是一个老问题,但当我试图记住如何做这件事时,我不断遇到它,所以我发布了这个答案,以便我能再次找到它!)

假设我们想要得到
obj.prop
的描述符,其中
类型(obj)是C

C.prop
通常起作用,因为描述符通常在通过
C
访问时返回自身(即绑定到
C
)。但是
C.prop
可能会触发其元类中的描述符。如果
prop
obj
中不存在,
obj.prop
将引发
AttributeError
,而
C.prop
可能不会。因此最好使用
inspect.getattr\u static(obj,'prop')

如果您对此不满意,这里有一个特定于CPython的方法(来自
对象/object.c
中的
对象\u GenericGetAttrWithDict
):


type\u lookup(type(obj),'prop')
返回描述符的方式与CPython在
obj使用描述符的方式相同。prop
如果
obj
是常用对象(例如,不是类)。

有时需要描述符对象?这违反了描述符的核心期望:它们应该看起来像属性。为什么要打破这一基本预期?为什么要这样做?为什么要创造这么复杂的东西?我现在做的对我来说并不复杂,但我想你可以说我在试验设计。在我目前的特殊情况下,我有一个描述符,返回游戏中武器的强度。该值是描述符状态(武器强度)和实例(舰船健康状况)的函数。武器种类繁多,;通常我只想要值结果,但在少数情况下,我需要知道它是什么类型的武器——描述符的类型。如果一个描述符的方法不是描述符协议的一部分,而你想调用它们呢;很高兴知道。函数本身将是一个例外,一个该模式的例外,但可能是唯一的例外。虽然它没有准确地回答我的问题,但你的回答确实帮助我解决了潜在的问题。我接受它。函数是这个模式的一个例外吗(我假设你谈论的是方法)?否,
c.method
从描述返回绑定方法,而
c.method
返回未绑定方法。这是相同的模式。函数并不完全遵循此模式,但非常相似:
函数。
返回未绑定的方法,而不是其本身。
staticmethod
classmethod
也打破了这一点。
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)