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
我假设后者似乎很自然。我确信这是有充分理由的。它是什么?
答案 0 :(得分:49)
你不能在getattr函数中放一个句点,因为getattr就像访问对象的字典查找一样(但由于子类化和其他Python实现细节,它比这更复杂一点)。
如果在a上使用'dir'函数,您将看到与对象属性对应的字典键。在这种情况下,字符串“b.c”在字典键集合中不是。
使用getattr
执行此操作的唯一方法是嵌套调用:
getattr(getattr(a, "b"), "c")
幸运的是,标准库有更好的解决方案!
import operator
operator.attrgetter("b.c")(a)
答案 1 :(得分:42)
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'>
答案 2 :(得分:7)
我认为你的困惑源于直点符号(ex 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
无法正常工作的原因。
答案 3 :(得分:6)
因为getattr
不起作用。 getattr
获取具有给定名称(第二个参数)的给定对象(第一个参数)的属性。所以你的代码:
getattr(a, "b.c") # this raises an AttributeError
表示:访问“a”引用的对象的“b.c”属性。显然,您的对象没有名为“b.c
”的属性。
要获得“c”属性,您必须使用两个getattr
来电:
getattr(getattr(a, "b"), "c")
让我们打开它以便更好地理解:
b = getattr(a, "b")
c = getattr(b, "c")
答案 4 :(得分:3)
我认为实现目标最直接的方法是使用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'
答案 5 :(得分:0)
应该返回getattr('a.b', {'a': None}, 'default-value'}
的内容?它应该引发AttributeError
还是只返回'default-value'
?这就是为什么在getattr
中引入复杂密钥会使其使用起来模糊不清的原因。
因此,将getattr(..)
函数视为对象属性字典的get
方法更为自然。
答案 6 :(得分:0)
您可以通过拆分点运算符并为每个点运算符执行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
如果您想调用a.b.c.d,可以通过a.multi_getattr('b.c.d')来完成。这将概括操作而不用担心字符串中的点操作计数。