2010-10-25 140 views
67

我想寫一個自定義類,其行爲類似於dict--所以我從dict繼承。一個類似字典的python類

但我的問題是:我是否需要在我的__init__()方法中創建私人dict成員?我沒有看到這一點,因爲我已經有dict行爲,如果我只是從dict繼承。

任何人都可以指出爲什麼大部分繼承片段看起來像下面那樣?

class CustomDictOne(dict): 
    def __init__(self): 
     self._mydict = {} 

    # other methods follow 

取而代之的是更簡單...

class CustomDictTwo(dict): 
    def __init__(self): 
     # initialize my other stuff here ... 

    # other methods follow 

其實,我覺得我懷疑這個問題的答案是,使用戶可以直接訪問不是你的字典(即它們必須使用訪問方法你提供的)。

但是,怎麼樣的數組訪問運算符[]?如何實現呢?到目前爲止,我還沒有看到一個示例說明如何覆蓋[]運算符。

因此,如果在自定義類中未提供[]訪問函數,繼承的基本方法將在不同的字典上運行?

我嘗試下面的代碼片段來測試我的Python繼承的理解:

class myDict(dict): 
    def __init__(self): 
     self._dict = {} 

    def add(self, id, val): 
     self._dict[id] = val 


md = myDict() 
md.add('id', 123) 
print md[id] 

我得到了以下錯誤:

KeyError: < built-in function id>

什麼是錯上面的代碼?

如何更正類myDict以便我可以編寫這樣的代碼?

md = myDict() 
md['id'] = 123 

[編輯]

我已編輯的代碼示例上述擺脫之前我虛線從我的辦公桌前我做了愚蠢的錯誤。這是一個錯字(我應該從錯誤信息中發現它)。

+23

我經常被稱爲「行爲像字典的程序員。 – 2010-10-25 12:44:53

+1

可能重複[Python:如何「完美」重寫一個字典](http://stackoverflow.com/questions/3387691/python-how-to-perfectly-override-a-dict) – 2015-01-06 21:57:04

回答

2

與此塊的代碼的問題:

class myDict(dict): 
    def __init__(self): 
     self._dict = {} 

    def add(id, val): 
     self._dict[id] = val 


md = myDict() 
md.add('id', 123) 

...是你的「添加」的方法(...你想成爲一個類的成員的任何方法)需要有一個明確的「自我」宣佈爲它的第一個參數,如:

def add(self, 'id', 23): 

要實現運算符重載通過密鑰才能訪問項目,看在docs的魔術方法__getitem____setitem__

請注意,由於Python使用Duck Typing,實際上可能沒有理由從語言的dict類派生自定義的dict類 - 而無需知道更多關於您想要做什麼的事情(例如,如果您需要通過這個類的一個實例放入某個代碼中,除非isinstance(MyDict(), dict) == True),否則只需實現使您的類充分類字體化並停止在那裏的API就可能會更好。

52

像這樣

class CustomDictOne(dict): 
    def __init__(self,*arg,**kw): 
     super(CustomDictOne, self).__init__(*arg, **kw) 

現在你可以使用內置的功能,如dict.get()self.get()。您不需要隱藏self._dict。您的班級已經 a字典。

+0

這個。沒有先調用它的構造函數從''dict''繼承是沒有意義的。 – sykora 2010-10-25 14:19:34

+1

請注意,您的繼承'dict'實際上包含2個dict-instance:第1個是繼承的容器,第2個是持有類屬性的dict - 您可以通過使用[__slots__](https:// docs。 python.org/3/reference/datamodel.html#slots)。 – ankostis 2016-03-04 10:37:19

+1

'__dict__'實際上只在第一次訪問時才被創建,所以只要用戶不嘗試使用它,就沒有問題。 '__slots__'雖然不錯。 – 2017-01-08 00:41:21

66
class Mapping(dict): 

    def __setitem__(self, key, item): 
     self.__dict__[key] = item 

    def __getitem__(self, key): 
     return self.__dict__[key] 

    def __repr__(self): 
     return repr(self.__dict__) 

    def __len__(self): 
     return len(self.__dict__) 

    def __delitem__(self, key): 
     del self.__dict__[key] 

    def clear(self): 
     return self.__dict__.clear() 

    def copy(self): 
     return self.__dict__.copy() 

    def has_key(self, k): 
     return k in self.__dict__ 

    def update(self, *args, **kwargs): 
     return self.__dict__.update(*args, **kwargs) 

    def keys(self): 
     return self.__dict__.keys() 

    def values(self): 
     return self.__dict__.values() 

    def items(self): 
     return self.__dict__.items() 

    def pop(self, *args): 
     return self.__dict__.pop(*args) 

    def __cmp__(self, dict_): 
     return self.__cmp__(self.__dict__, dict_) 

    def __contains__(self, item): 
     return item in self.__dict__ 

    def __iter__(self): 
     return iter(self.__dict__) 

    def __unicode__(self): 
     return unicode(repr(self.__dict__)) 


o = Mapping() 
o.foo = "bar" 
o['lumberjack'] = 'foo' 
o.update({'a': 'b'}, c=44) 
print 'lumberjack' in o 
print o 

In [187]: run mapping.py 
True 
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44} 
+13

如果你要繼承'dict',那麼你應該使用對象本身(使用'super')而不是簡單地委託給實例的'__dict__' - 這意味着你要爲每個對象創建兩個字典實例。 – 2017-01-08 00:39:48

6

爲了完整起見,這裏是鏈接到由@提到的文件比約恩 - 博動獲得最新的Python 2.x的(2.7.7截至截稿時):

Emulating Container Types

(對不起,不使用評論功能,我只是不被允許通過計算器這樣做。)