python 属性查找没有任何描述符魔法?
我已经开始在我编写的代码中更广泛地使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
大多数描述符仅在作为实例属性访问时才完成其工作。因此,当类访问它时,返回自身很方便:
这允许您获取描述符本身:
注意,这也适用于(大多数?)内置描述符:
当您需要在子类中扩展它时,访问描述符可能很有用,但有一个更好的方法来做到这一点。
Most descriptors do their job when accessed as instance attribute only. So it's convenient to return itself when it's accessed for class:
This allows you to get descriptor itself:
Note, that this works for (most?) built-in descriptors too:
Accessing descriptor could be useful when you need to extent it in subclass, but there is a better way to do this.
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!)
上述方法
每当您控制属性的代码时, 都是一个很好的方法,但在某些情况下,例如当属性是由其他人控制的库的一部分时,另一种方法很有用。这种替代方法在其他情况下也很有用,例如实现对象映射、遍历问题中描述的名称空间或其他专用库。
考虑一个具有简单属性的类:
当从容器对象类dict访问时,“描述符魔法”被绕过。另请注意,如果我们将该属性分配给一个新的类变量,它的行为就像带有“描述符魔法”的原始变量一样,但如果分配给一个实例变量,该属性的行为就像任何普通对象一样,并且也会绕过“描述符魔法”。
The above method
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:
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'.
假设我们想要获取
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
):type_lookup(type(obj), ' prop')
如果obj
是普通对象(例如,不是类),则 CPython 在 obj.prop 中使用描述符时以相同的方式返回描述符。Let's say we want to get the descriptor for
obj.prop
wheretype(obj) is C
.C.prop
usually works because the descriptor usually returns itself when accessed viaC
(i.e., bound toC
). ButC.prop
may trigger a descriptor in its metaclass. Ifprop
were not present inobj
,obj.prop
would raiseAttributeError
whileC.prop
might not. So it's better to useinspect.getattr_static(obj, 'prop')
.If you are not satisfied with that, here's a CPython-specific method (from
_PyObject_GenericGetAttrWithDict
inObjects/object.c
):type_lookup(type(obj), 'prop')
returns the descriptor in the same way when CPython uses it atobj.prop
ifobj
is a usual object (not class, for example).