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
对我来说,假设后者似乎很自然。我确信这是有充分理由的。它是什么?
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 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
是语法错误,因为现在您要求解释器将这些内容解析为原始代码,但这不起作用。
另一面是 whilefoo.b.c
等价于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”属性。显然,您的对象没有名为“ b.c
”的属性。
要获得“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() 来调用多个 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') 来实现。这将概括操作而不用担心字符串中点操作的计数。
应该返回getattr('a.b', {'a': None}, 'default-value'}
什么?它应该提高AttributeError
还是返回'default-value'
?这就是为什么如果引入复杂的键getattr
会使其难以使用。
因此,将getattr(..)
函数视为get
对象属性字典的方法更为自然。