屬性樹
與你的第一個規範的問題是,Python不能在__getitem__
告訴我們,如果在my_obj.a.b.c.d
,你接下來會進一步繼續向下一個不存在的樹,在這種情況下,需要返回一個具有__getitem__
方法的對象,因此您不會得到AttributeError
,或者如果您需要值,則需要返回None
。
我會爭辯說,在上述每種情況下,您都應該期望它會拋出KeyError
而不是返回None
。原因是你不知道None
是否意味着「沒有鑰匙」或「在該位置實際存儲了None
」。對於這種行爲,所有你需要做的就是採取dotdictify
,刪除,並將其替換__getitem__
:
def __getitem__(self, key):
return self[key]
因爲你真正想要的是一個dict
與__getattr__
和__setattr__
。
可能有辦法完全去除__getitem__
和這樣說__getattr__ = dict.__getitem__
,但我覺得這可能是過度優化,並且將是一個問題,如果你以後決定要__getitem__
創建樹,因爲它是這樣dotdictify
本來呢,在這種情況下,你將其更改爲:
def __getitem__(self, key):
if key not in self:
dict.__setitem__(self, key, dotdictify())
return dict.__getitem__(self, key)
我不喜歡業務在原有dotdictify
。
路徑支持
的第二個參數(覆蓋get()
和set()
)是正常的dict
有get()
,從你的描述不同地工作,甚至沒有一個set
(儘管它有一個setdefault()
這是與get()
相反的操作)。人們預計get
需要兩個參數,第二個參數是默認值,如果找不到密鑰。
如果你想擴展__getitem__
和__setitem__
處理點鍵符號,你將需要修改doctictify
到:
class dotdictify(dict):
def __init__(self, value=None):
if value is None:
pass
elif isinstance(value, dict):
for key in value:
self.__setitem__(key, value[key])
else:
raise TypeError, 'expected dict'
def __setitem__(self, key, value):
if '.' in key:
myKey, restOfKey = key.split('.', 1)
target = self.setdefault(myKey, dotdictify())
if not isinstance(target, dotdictify):
raise KeyError, 'cannot set "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
target[restOfKey] = value
else:
if isinstance(value, dict) and not isinstance(value, dotdictify):
value = dotdictify(value)
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if '.' not in key:
return dict.__getitem__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
raise KeyError, 'cannot get "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
return target[restOfKey]
def __contains__(self, key):
if '.' not in key:
return dict.__contains__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
return False
return restOfKey in target
def setdefault(self, key, default):
if key not in self:
self[key] = default
return self[key]
__setattr__ = __setitem__
__getattr__ = __getitem__
測試代碼:
>>> life = dotdictify({'bigBang': {'stars': {'planets': {}}}})
>>> life.bigBang.stars.planets
{}
>>> life.bigBang.stars.planets.earth = { 'singleCellLife' : {} }
>>> life.bigBang.stars.planets
{'earth': {'singleCellLife': {}}}
>>> life['bigBang.stars.planets.mars.landers.vikings'] = 2
>>> life.bigBang.stars.planets.mars.landers.vikings
2
>>> 'landers.vikings' in life.bigBang.stars.planets.mars
True
>>> life.get('bigBang.stars.planets.mars.landers.spirit', True)
True
>>> life.setdefault('bigBang.stars.planets.mars.landers.opportunity', True)
True
>>> 'landers.opportunity' in life.bigBang.stars.planets.mars
True
>>> life.bigBang.stars.planets.mars
{'landers': {'opportunity': True, 'vikings': 2}}
非常感謝,邁克。我添加了一個get函數,它接受點符號(以及默認值,如您所注意到的),我認爲這個新的dotdictify類將使處理深度嵌套字典的生活變得更容易。非常感謝。 – Hal 2010-09-27 01:23:56
你需要一個'get()'函數嗎?它做什麼,現有的'get()'不?你在問題中描述的'get()'相當於你從'dict'免費獲得的'get(key,None)'。 – 2010-09-27 01:40:02
當我使用dottimetify類「as-is」w/my Python 2.5安裝(Google App Engine SDK)時,get函數由於某種原因未處理點符號請求。所以我爲get()函數編寫了一個快速封裝來檢查點符號,如果是,傳遞給__getattr__(返回默認的異常),否則傳遞給dict.get(self,key,default) – Hal 2010-09-27 18:15:44