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

发布于 2024-08-09 04:37:34 字数 2406 浏览 1 评论 0原文

我已经开始在我编写的代码中更广泛地使用 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)

I've started to use the python descriptor protocol more extensively in the code I've been writing. Typically, the default python lookup magic is what I want to happen, but sometimes I'm finding I want to get the descriptor object itself instead the results of its __get__ method. Wanting to know the type of the descriptor, or access state stored in the descriptor, or somesuch thing.

I wrote the code below to walk the namespaces in what I believe is the correct ordering, and return the attribute raw regardless of whether it is a descriptor or not. I'm surprised though that I can't find a built-in function or something in the standard library to do this -- I figure it has to be there and I just haven't noticed it or googled for the right search term.

Is there functionality somewhere in the python distribution that already does this (or something similar)?

Thanks!

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

Edit Wed, Oct 28, 2009.

Denis's answer gave me a convention to use in my descriptor classes to get the descriptor objects themselves. But, I had an entire class hierarchy of descriptor classes, and I didn't want to begin every __get__ function with a boilerplate

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

To avoid this, I made the root of the descriptor class tree inherit from the following:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

美男兮 2024-08-16 04:37:34

大多数描述符仅在作为实例属性访问时才完成其工作。因此,当类访问它时,返回自身很方便:

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>

当您需要在子类中扩展它时,访问描述符可能很有用,但有一个更好的方法来做到这一点。

Most descriptors do their job when accessed as instance attribute only. So it's convenient to return itself when it's accessed for class:

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

This allows you to get descriptor itself:

>>> 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'

Note, that this works for (most?) built-in descriptors too:

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

Accessing descriptor could be useful when you need to extent it in subclass, but there is a better way to do this.

℡Ms空城旧梦 2024-08-16 04:37:34

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

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

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

The inspect library provides a function to retrieve an attribute without any descriptor magic: inspect.getattr_static.

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

(This is an old question, but I keep coming across it when trying to remember how to do this, so I'm posting this answer so I can find it again!)

故事未完 2024-08-16 04:37:34

上述方法

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访问时,“描述符魔法”被绕过。另请注意,如果我们将该属性分配给一个新的类变量,它的行为就像带有“描述符魔法”的原始变量一样,但如果分配给一个实例变量,该属性的行为就像任何普通对象一样,并且也会绕过“描述符魔法”。

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

The above method

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

Is a great method whenever you control the code of the property, but there are some cases, such as when the property is part of a library controlled by someone else, where another approach is useful. This alternative approach can also be useful in other situations such as implementing object mapping, walking a name-space as described in the question, or other specialised libraries.

Consider a class with a simple property:

class ClassWithProp:

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

When accessed from the container objects class dict, the 'descriptor magic' is bypassed. Note also that if we assign the property to a new class variable, it behaves just like the original with 'descriptor magic', but if assigned to an instance variable, the property behaves as any normal object and also bypasses 'descriptor magic'.

>>> test.__class__.classvar =  test.__class__.__dict__['value']
>>> test.classvar
3
>>> test.instvar = test.__class__.__dict__['value']
>>> test.instvar
<property object at 0x00000216A39D0778>
岁月静好 2024-08-16 04:37:34

假设我们想要获取 obj.prop 的描述符,其中 type(obj) 是 C

C.prop 通常有效,因为描述符在通过 C 访问时通常会返回自身(即绑定到 C)。但是C.prop可能会触发其元类中的描述符。如果 obj 中不存在 prop,则 obj.prop 将引发 AttributeError,而 C.prop< /code> 可能不会。所以最好使用inspect.getattr_static(obj, 'prop')

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

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 是普通对象(例如,不是类),则 CPython 在 obj.prop 中使用描述符时以相同的方式返回描述符。

Let's say we want to get the descriptor for obj.prop where type(obj) is C.

C.prop usually works because the descriptor usually returns itself when accessed via C (i.e., bound to C). But C.prop may trigger a descriptor in its metaclass. If prop were not present in obj, obj.prop would raise AttributeError while C.prop might not. So it's better to use inspect.getattr_static(obj, 'prop').

If you are not satisfied with that, here's a CPython-specific method (from _PyObject_GenericGetAttrWithDict in 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') returns the descriptor in the same way when CPython uses it at obj.prop if obj is a usual object (not class, for example).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文