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`不支持连续的属性检索?
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函数中放置句点,因为getattr是对象的直接字典查找。
如果您在a上使用'dir'功能,您会看到与您的对象属性相对应的字典键。在这种情况下,字典键集合中的“b.c”不是。
与getattr
做到这一点的唯一方法是嵌套调用:
getattr(getattr(a, "b"), "c")
幸运的是,标准库有一个更好的解决方案!
import operator
operator.attrgetter("b.c")(a)
OP已经意识到......我想他是在问为什么。不是一个无理由的问题operator.attrgetter('a.b')(obj)_will_ resolve dotted notation – 2012-08-15 19:28:09
感谢您指出。澄清我的回应,希望更好地解释我的意思。 – 2012-08-15 20:28:28
这是一个非常好的和简单的解决一个令人不安的问题。下面的答案并不简单。这个答案不尝试去实现Python已经做了的事情,这使得它很好。 – Bobort 2016-10-20 14:55:00
因为getattr
不能这样工作。 getattr
使用给定名称(第二个参数)获取给定对象(第一个参数)的属性。所以,你的代码:
getattr(a, "b.c") # this raises an AttributeError
表示:访问 “b.c” 由 “一”引用对象的属性。显然你的对象没有叫做“b.c
”的属性。
得到 “C” 的属性,你必须使用两个getattr
电话:
getattr(getattr(a, "b"), "c")
让我们来解开它更好的理解:
b = getattr(a, "b")
c = getattr(b, "c")
什么应该返回getattr('a.b', {'a': None}, 'default-value'}
?它应该提高AttributeError
还是只返回'default-value'
?这就是为什么如果在getattr
中引入复杂的密钥会使其难以使用。
因此,将getattr(..)
函数看作get
对象属性字典的方法更自然。
我认为你的困惑是由于直点符号(例如a.b.c
)访问与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.b.c
相当于foo.__dict__['b'].__dict__['c']
,getattr(foo, 'b.c')
相当于foo.__dict__['b.c']
。这就是为什么getattr
不能像你期望的那样工作。
天哪,我看到这发生在一个混淆的代码比赛... – 2012-08-21 03:48:40
Python's built-in reduce
function启用您正在查找的功能。这里有一个简单的小帮手功能,可以完成这项工作:
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'>
这应该是接受的答案imho,从塔那的答案是旁边没用。 – sleepycal 2014-03-12 16:15:58
我都很好的回答,这样就完成了工作。但是,这个答案并没有给OP提供什么他正在寻找的原因 - 它不起作用的原因。 – 2014-03-12 16:55:24
接受的答案不能解决原始问题。假设你只给了字符串“b.c”,这是解决问题的唯一答案。 – 2014-05-02 14:16:17
可以调用多个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')来完成。这将推广操作而不用担心字符串中的点操作的数量。
我认为实现你想要的最直接的方法是使用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'
现在这样做:'SETATTR(一, 'b.c',2)'。 getattr(a,'b.c')'现在应该返回什么?如果在'b'之前没有'c',怎么办?你可以在属性名称中使用'.',所以你不能指望'getattr'能够像这样遍历对象。 – 2012-08-15 19:27:18