![](/img/trans.png)
[英]Does getattr() always call existing __getattr__() even if an attribute exists?
[英]Why does `getattr` not support consecutive attribute retrievals?
class A(): pass
a = A()
b = A()
a.b = b
b.c = 1
a.b # this is b
getattr(a, "b") # so is this
a.b.c # this is 1
getattr(a, "b.c") # this raises an AttributeError
在我看来,接受后者似乎很自然。 我相信这是有充分理由的。 它是什么?
您不能在getattr函数中添加句点,因为getattr就像访问对象的字典查找(但由于子类化和其他Python实现细节,因此比之复杂一些)。
如果在a上使用“ dir”功能,则会看到与对象属性相对应的字典键。 在这种情况下,字符串“ bc” 不在字典键集中。
使用getattr
做到这一点的唯一方法是嵌套调用:
getattr(getattr(a, "b"), "c")
幸运的是,标准库有更好的解决方案!
import operator
operator.attrgetter("b.c")(a)
Python的内置reduce
函数可实现您正在寻找的功能。 这是一个简单的小助手功能,可以完成工作:
class NoDefaultProvided(object):
pass
def getattrd(obj, name, default=NoDefaultProvided):
"""
Same as getattr(), but allows dot notation lookup
Discussed in:
http://stackoverflow.com/questions/11975781
"""
try:
return reduce(getattr, name.split("."), obj)
except AttributeError, e:
if default != NoDefaultProvided:
return default
raise
测试证明;
>>> getattrd(int, 'a')
AttributeError: type object 'int' has no attribute 'a'
>>> getattr(int, 'a')
AttributeError: type object 'int' has no attribute 'a'
>>> getattrd(int, 'a', None)
None
>>> getattr(int, 'a', None)
None
>>> getattrd(int, 'a', None)
None
>>> getattrd(int, '__class__.__name__')
type
>>> getattrd(int, '__class__')
<type 'type'>
我认为您的困惑是由于以下事实引起的:直接点表示法(ex abc
)与getattr()
访问相同的参数,但是解析逻辑不同。 尽管它们本质上都键入对象的__dict__
属性,但getattr()
并不限于对点可访问属性的更严格要求。 例如
setattr(foo, 'Big fat ugly string. But you can hash it.', 2)
是有效的,因为该字符串只是成为foo.__dict__
的哈希键,但是
foo.Big fat ugly string. But you can hash it. = 2
和
foo.'Big fat ugly string. But you can hash it.' = 2
语法错误,因为现在您要让解释器将这些内容解析为原始代码,但这是行不通的。
foo.bc
一面是,虽然foo.bc
等同于foo.__dict__['b'].__dict__['c']
,但getattr(foo, 'b.c')
等同于foo.__dict__['b.c']
。 这就是为什么getattr
不能按您期望的那样工作。
因为getattr
不能那样工作。 getattr
获取具有给定名称(第二个参数)的给定对象(第一个参数)的属性。 所以你的代码:
getattr(a, "b.c") # this raises an AttributeError
表示: 访问“ a”引用的对象的“ bc”属性 。 显然,您的对象没有名为“ bc
”的属性。
要获取“ c”属性,必须使用两个getattr
调用:
getattr(getattr(a, "b"), "c")
让我们对其进行包装以更好地理解:
b = getattr(a, "b")
c = getattr(b, "c")
我认为实现所需目标的最直接方法是使用operator.attrgetter
。
>>> import operator
>>> class B():
... c = 'foo'
...
>>> class A():
... b = B()
...
>>> a = A()
>>> operator.attrgetter('b.c')(a)
'foo'
如果该属性不存在,则将出现AttributeError
>>> operator.attrgetter('b.d')(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: B instance has no attribute 'd'
应该返回什么getattr('a.b', {'a': None}, 'default-value'}
?它应该引发AttributeError
还是只返回'default-value'
?这就是为什么如果在getattr
引入了复杂的键会使它变晦涩的使用。
因此,将getattr(..)
函数视为对象属性字典的get
方法更为自然。
您可以通过分割点运算符并为每个点运算符执行getattr()来调用多个getattr而无需在函数内调用函数
def multi_getattr(self,obj, attr, default = None):
attributes = attr.split(".")
for i in attributes:
try:
obj = getattr(obj, i)
except AttributeError:
if default:
return default
else:
raise
return obj
如果您想调用abcd,则可以通过a.multi_getattr('bcd')进行。 这将使操作通用化,而不必担心字符串中一个点运算的数量。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.