2013-08-27 43 views
2

我還沒有看到字典點訪問的切換版本。可切換點訪問Python字典?

我第一遍這裏嘗試不起作用:

class DottableDict (dict): 

    def allowDotting (self, state=True): 
     if state: 
      self.__setattr__ = dict.__setitem__ 
      self.__getattr__ = dict.__getitem__ 
     else: 
      del self.__setattr__ 
      del self.__getattr__ 

>>> import dot 
>>> d = dot.DottableDict() 
>>> d.allowDotting() 
>>> d.foo = 'bar' 
>>> d 
{} 
>>> d.foo 
'bar' 
>>> d.__dict__ 
{'__setattr__': <slot wrapper '__setitem__' of 'dict' objects>, 'foo': 'bar', 
'__getattr__': <method '__getitem__' of 'dict' objects>} 
>>> d.allowDotting(False) 
>>> d.__dict__ 
{'foo': 'bar'} 

我認爲簽名不SETATTR和setitem之間的匹配。

我第二次也似乎像它應該工作,但在相同的方式失敗:

class DottableDict (dict): 

    def dotGet (self, attr): 
     return dict.__getitem__(self, attr) 

    def dotSet (self, attr, value): 
     return dict.__setitem__(self, attr, value) 

    def allowDotting (self, state=True): 
     if state: 
      self.__getattr__ = self.dotGet 
      self.__setattr__ = self.dotSet 
     else: 
      del self.__setattr__ 
      del self.__getattr__ 
+0

你想要什麼樣的行爲? – unutbu

+0

我有時想使用字典點訪問 - 通過instance.foo作爲實例['foo']的替代方式訪問字典派生的實例值 - 但我希望能夠在運行時打開和關閉此功能。 –

回答

6

如果設置self.__dict__ = self,那麼字典將自動成爲「dottable」。您可以通過設置self.__dict__ = {}關閉「點能力」。然而,鍵值對仍然可以通過索引訪問。這個想法主要來自katrielalex's bio page

class DottableDict(dict): 
    def __init__(self, *args, **kwargs): 
     dict.__init__(self, *args, **kwargs) 
     self.__dict__ = self 
    def allowDotting(self, state=True): 
     if state: 
      self.__dict__ = self 
     else: 
      self.__dict__ = dict() 

d = DottableDict() 
d.allowDotting() 
d.foo = 'bar' 

print(d['foo']) 
# bar 
print(d.foo) 
# bar 

d.allowDotting(state=False) 
print(d['foo']) 
# bar 
print(d.foo) 
# AttributeError: 'DottableDict' object has no attribute 'foo'   

但是記住了Python的禪:

應該有one-- 和最好只有一個 --obvious辦法做到這一點。

通過將自己限制爲詞典訪問的標準語法,可以提高自己和其他人的可讀性/可維護性。


工作原理:

當你鍵入d.foo,巨蟒在許多地方,其中之一是在d.__dict__查找'foo'。 (它也在d.__class__.__dict__以及d.__class__.mro()中列出的所有基地中的所有__dict__ ...有關屬性查找的完整詳細信息,請參閱此excellent article by Shalabh Chaturvedi)。

無論如何,對我們來說重要的一點是d.__dict__中的所有鍵值對都可以用點符號來訪問。

這個事實意味着我們可以通過設置d.__dict__d本身來獲得對d中的鍵值對的點訪問! d,畢竟是字典,而d.__dict__需要類似字典的對象。注意這也是高效的內存。我們沒有複製任何鍵值對,我們正在簡化d.__dict__到一個已經存在的字典。

此外,通過將d.__dict__分配給dict(),我們實際上關閉了對d中鍵值對的點訪問。 (例如,這並不完全禁用d.__class_.__dict__中的點訪問 - 鍵值對,仍然可以通過點符號訪問。謝天謝地這是真的,否則您將無法再次調用allowDotting方法!)

現在您可能想知道這是否會刪除d本身中的所有鍵值對。答案是不。

鍵值對未存儲在__dict__屬性中。實際上,一個正常的字典沒有__dict__屬性。所以設置d.__dict__ = {}只需將字典重置爲中性條件。我們可以使用的

self.__dict__ = dict() 

del self.__dict__ 

insteaad了。但是,由於DottableDict__init__中給出__dict__屬性,因此允許DottableDict的實例總是具有__dict__屬性,這對我來說似乎更清潔。


在評論您注意:

步驟:關閉訪問,設置d.foo爲 '巴',打開訪問,d.foo從到處走 。

其設定而allowDotting要保留的屬性,如d.foo已被關閉,你需要存儲的備用字典到self.__dict__已設置。

class DottableDict(dict): 
    def __init__(self, *args, **kwargs): 
     dict.__init__(self, *args, **kwargs) 
     self['_attributes'] = dict() 
     self.allowDotting() 
    def allowDotting(self, state=True): 
     if state: 
      self.update(self['_attributes']) 
      self.__dict__ = self 
     else: 
      self.__dict__ = self['_attributes'] 

d = DottableDict() 
d.allowDotting(state=False) 
d.foo = 'bar' 
d.allowDotting(state=True) 
print(d.foo) 
# bar 
d.allowDotting(state=False) 
print(d.foo) 
# bar 
d.allowDotting(state=True) 
print(d.foo) 
# bar 

按照慣例,以單個下劃線開頭的屬性被理解爲私有實現細節。我通過在字典中引入私鑰'_attribute'來擴展這個約定。

+0

這看起來更像是我期待的代碼量。我只是想知道python在什麼地方放置東西,但並不完全理解它。爲什麼將'self .__ dict__'設置爲'{}'將事物切換回attr查找? –

+0

我已經在上面添加了一些解釋。 – unutbu

+0

我覺得我看到'__dict__'沒有通過點查找來搜索,但是我做了一個'class A(object):pass',然後'a = A()','setattr(a,'foo' ,'bar')'和'print a.foo',它會吐出'bar',所以我想我錯了。我有更多的問題,但我會先閱讀你給我的鏈接。之後的任何剩餘問題,我將作爲SO的新問題提出。我接受這個答案,因爲它回答了這個問題,是最小和最靈活的,並消除了我所面臨的所有邊緣條件。謝謝。 –