2015-04-01 43 views
1

在Python 3中,我有一個從另一個庫中獲得的列表和字典樹。我想用包含更多行爲的對象來測試樹中的字典(給簡單的字典類提供更豐富的模型)。我試着更換類這些對象與字典的一個子類,但這是不允許的:在Python中委派一個字典類

class MyClass(dict): pass 
{}.__class__ = MyClass 

失敗與TypeError: __class__ assignment: only for heap types

所以我不是嘗試編寫一個包裝或適配器或委託類:

class InstrumentedDict(object): 
    """ 
    Instrument an existing dictionary with additional 
    functionality, but always reference and mutate 
    the original dictionary. 

    >>> orig = {'a': 1, 'b': 2} 
    >>> inst = InstrumentedDict(orig) 
    >>> hasattr(inst, '__getitem__') 
    True 
    >>> inst.__getitem__('a') 
    1 
    >>> inst['a'] 
    1 
    >>> inst['c'] = 3 
    >>> orig['c'] 
    3 
    >>> inst.keys() == orig.keys() 
    True 
    """ 
    def __init__(self, orig): 
     self._orig = orig 

    def __getattribute__(self, name): 
     orig = super(InstrumentedDict, self).__getattribute__('_orig') 
     return orig.__getattribute__(name) 

然而,在文檔測試失敗inst['a']TypeError: 'InstrumentedDict' object is not subscriptable。但請注意,它不會調用__hasattr____getitem__

我希望將所有行爲委託給底層字典,我不想考慮或明確委託字典的整個簽名。

無論這個類做什麼都會影響底層字典(而不是單獨創建對這些值的引用),這一點很重要。理想情況下,它不應該強制或否定底層映射的可變性,而應該反映其行爲。

是否有一個簡單而優雅的解決方案符合指定的界面,但不需要顯式鏡像簽名(如在this implementation中)?爲了澄清,我想覆蓋現有字典上的行爲而不創建新的副本,例如,如果修改了檢測副本,原始副本也會被修改。

+0

我沒有看到'__getitem__' ....或'__setitem__' ... – kindall 2015-04-01 21:27:27

+0

'inst .__ getitem __('a')'和'inst ['a']'是後者將始終在類中搜索實際的'__getitem__'(考慮到您正在使用新的樣式類)。 – 2015-04-01 21:33:39

+1

你可能需要實現類似http://stackoverflow.com/questions/9942536/how-to-fake-proxy-a-class-in-python如果你想要一個真正的代理... – 2015-04-01 21:34:12

回答

3

在完全丟失了你的問題點的危險......

是否有任何理由建立一個代理,而不是僅僅繼承dict?喜歡的東西:評論後

class InstrumentedDict(dict): 
    """ Walks like a dict, talks like a dict... """ 

編輯:

啊,我看:)有道理......

好像UserDict就是答案,檢查了這一點:

from collections import UserDict 

class InstrumentedDict(UserDict): 

    def __init__(self, data): 
     super(InstrumentedDict, self).__init__() 
     self.data = data 

remote_dict = {"a": 1} 
instr_dict = InstrumentedDict(remote_dict) 

print(instr_dict) # {'a': 1} 

instr_dict["b"] = 2 

print(instr_dict) # {'a': 1, 'b': 2} 
print(remote_dict) # {'a': 1, 'b': 2} 

UserDict好像是b是我們無法直接劃分dict的古代遺物。但它很有用,因爲它暴露了data屬性。這幾乎是所有的文檔說:UserDict

+0

正確 - 因爲我無法使用InstrumentedDict覆蓋現有的字典。當源代碼字典被傳入時,它將進行復制。重要的是,原始字典是通過代理進行變異的。 – 2015-04-01 23:53:38

+0

@ JasonR.Coombs我用一個真正的代理更新了答案 – frnhr 2015-04-03 22:17:05

+0

@frnhr工作很好。我忽略了UserDict,因爲我對它的印象是它通過子類化dict而被消除了,但正如你現在已經證明的那樣,這裏有一個UserDict提供了子類化dict不能的靈活性的例子。 – 2015-04-04 20:39:08