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