首頁  >  文章  >  後端開發  >  Python中defaultdict的詳解(程式碼範例)

Python中defaultdict的詳解(程式碼範例)

不言
不言轉載
2018-10-25 17:34:433098瀏覽

這篇文章帶給大家的內容是關於Python中defaultdict的詳解(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

預設值可以很方便

眾所周知,在Python中如果存取字典中不存在的鍵,會引發KeyError異常(JavaScript中如果物件中不存在某個屬性,則傳回undefined)。但是有時候,字典中的每個鍵都存在預設值是非常方便的。例如下面的例子:

strings = ('puppy', 'kitten', 'puppy', 'puppy',
           'weasel', 'puppy', 'kitten', 'puppy')
counts = {}
for kw in strings:
    counts[kw] += 1

此範例統計strings中某個字出現的次數,並在counts字典中作記錄。單字每出現一次,在counts相對應的鍵所存的值數字加1。但事實上,運行這段程式碼會拋出KeyError異常,出現的時機是每個單字第一次統計的時候,因為Python的dict中不存在預設值的說法,可以在Python命令列中驗證:

>>> counts = dict()
>>> counts
{}
>>> counts['puppy'] += 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: &#39;puppy&#39;

使用判斷語句檢查

既然如此,首先可能想到的方法是在單字第一次統計的時候,在counts中對應的鍵存下預設值1。這需要在處理的時候加入一個判斷語句:
strings = (&#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;, &#39;puppy&#39;,
           &#39;weasel&#39;, &#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;)
counts = {}
for kw in strings:
    if kw not in counts:
        counts[kw] = 1
    else:
        counts[kw] += 1
# counts:
# {&#39;puppy&#39;: 5, &#39;weasel&#39;: 1, &#39;kitten&#39;: 2}

使用dict.setdefault()方法

也可以透過dict.setdefault()方法來設定預設值:

strings = (&#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;, &#39;puppy&#39;,
           &#39;weasel&#39;, &#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;)
counts = {}
for kw in strings:
    counts.setdefault(kw, 0)
    counts[kw] += 1

dict.setdefault()方法接收兩個參數,第一個參數是健的名稱,第二個參數是預設值。如果字典中不存在給定的鍵,則傳回參數中提供的預設值;反之,則傳回字典中儲存的值。利用dict.setdefault()方法的回傳值可以重寫for迴圈中的程式碼,使其更簡潔:

strings = (&#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;, &#39;puppy&#39;,
           &#39;weasel&#39;, &#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;)
counts = {}
for kw in strings:
    counts[kw] = counts.setdefault(kw, 0) + 1

使用collections.defaultdict類別

以上的方法雖然在一定程度上解決了dict中不存在預設值的問題,但是這時候我們會想,有沒有一種字典它本身提供了預設值的功能呢?答案是肯定的,那就是collections.defaultdict。

defaultdict類別就好像是dict,但是它是使用一個類型來初始化的:

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> dd
defaultdict(<type &#39;list&#39;>, {})

defaultdict類別的初始化函數接受一個類型作為參數,當所訪問的鍵不存在的時候,可以實例化一個值作為預設值:

>>> dd[&#39;foo&#39;]
[]
>>> dd
defaultdict(<type &#39;list&#39;>, {&#39;foo&#39;: []})
>>> dd[&#39;bar&#39;].append(&#39;quux&#39;)
>>> dd
defaultdict(<type &#39;list&#39;>, {&#39;foo&#39;: [], &#39;bar&#39;: [&#39;quux&#39;]})

需要注意的是,這種形式的預設值只有在透過dict[key]dict.__getitem__(key)訪問的時候才有效,這其中的原因在下文會介紹。

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> &#39;something&#39; in dd
False
>>> dd.pop(&#39;something&#39;)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: &#39;pop(): dictionary is empty&#39;
>>> dd.get(&#39;something&#39;)
>>> dd[&#39;something&#39;]
[]

該類別除了接受類型名稱作為初始化函數的參數之外,還可以使用任何不帶參數的可調用函數,到時該函數的返回結果作為預設值,這使得預設值的取值更加靈活。以下用一個例子來說明,如何用自訂的不帶參數的函數zero()作為初始化函數的參數:

>>> from collections import defaultdict
>>> def zero():
...     return 0
...
>>> dd = defaultdict(zero)
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {})
>>> dd[&#39;foo&#39;]
0
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {&#39;foo&#39;: 0})

利用collections.defaultdict來解決最初的單字統計問題,程式碼如下:

from collections import defaultdict
strings = (&#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;, &#39;puppy&#39;,
           &#39;weasel&#39;, &#39;puppy&#39;, &#39;kitten&#39;, &#39;puppy&#39;)
counts = defaultdict(lambda: 0)  # 使用lambda来定义简单的函数
for s in strings:
    counts[s] += 1

defaultdict 類別是如何實現的

透過上面的內容,想必大家已經了解了defaultdict類別的用法,那麼在defaultdict類別中又是如何來實現預設值的功能呢?這其中的關鍵是使用了看__missing__()這個方法:

>>> from collections import defaultdict
>>> print defaultdict.__missing__.__doc__
__missing__(key) # Called by __getitem__ for missing key; pseudo-code:
  if self.default_factory is None: raise KeyError(key)
  self[key] = value = self.default_factory()
  return value

透過查看__missing__()方法的docstring,可以看出當使用__getitem__()方法存取一個不存在的鍵時( dict[key]這個形式其實是__getitem__()方法的簡化形式),會呼叫__missing__()方法取得預設值,並將該鍵加入字典。

關於__missing__()方法的具體介紹可以參考Python官方文檔中的"Mapping Types — dict"一節。

文件中介紹,從2.5版本開始,如果派生自dict的子類別定義了__missing__()方法,當存取不存在的鍵時,dict[key]會呼叫__missing__()方法取得預設值.

從中可以看出,雖然dict支援__missing__()方法,但是在dict本身是不存在這個方法的,而是需要在派生的子類別中自行實作這個方法。可以簡單的驗證這一點:

>>> print dict.__missing__.__doc__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object &#39;dict&#39; has no attribute &#39;__missing__&#39;

同時,我們可以進一步的做實驗,定義一個子類別Missing並實作__missing__()方法:

>>> class Missing(dict):
...     def __missing__(self, key):
...         return 'missing'
...
>>> d = Missing()
>>> d
{}
>>> d['foo']
'missing'
>>> d
{}

傳回結果反映了__missing__( )方法確實發揮了作用。在此基礎上,我們稍許修改__missing__()方法,使得該子類別同defautldict類別一樣為不存在的鍵設定一個預設值:

>>> class Defaulting(dict):
...     def __missing__(self, key):
...         self[key] = &#39;default&#39;
...         return &#39;default&#39;
...
>>> d = Defaulting()
>>> d
{}
>>> d[&#39;foo&#39;]
&#39;default&#39;
>>> d
{&#39;foo&#39;: &#39;default&#39;}

在舊版本的Python 中實作defaultdict 的功能

defaultdict類別是從2.5版本之後才添加的,在一些舊版本中並不支援它,因此為舊版本實作一個相容的defaultdict類別是必要的。這其實很簡單,雖然效能可能未必如2.5版本中自帶的defautldict類別好,但在功能上是一樣的。

首先,__getitem__()方法需要在存取鍵失敗時,呼叫__missing__()方法:

class defaultdict(dict):
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)

其次,需要實作__missing__()方法用來設定預設值:

class defaultdict(dict):
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)
    def __missing__(self, key):
        self[key] = value = self.default_factory()
        return value

然後,defaultdict類別的初始化函數__init__()需要接受類型或可呼叫函數參數:

class defaultdict(dict):
    def __init__(self, default_factory=None, *a, **kw):
        dict.__init__(self, *a, **kw)
        self.default_factory = default_factory    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)
    def __missing__(self, key):
        self[key] = value = self.default_factory()
        return value

最后,综合以上内容,通过以下方式完成兼容新旧Python版本的代码:

try:
    from collections import defaultdictexcept ImportError:
    class defaultdict(dict):
      def __init__(self, default_factory=None, *a, **kw):
          dict.__init__(self, *a, **kw)
          self.default_factory = default_factory      def __getitem__(self, key):
          try:
              return dict.__getitem__(self, key)
          except KeyError:
              return self.__missing__(key)

      def __missing__(self, key):
          self[key] = value = self.default_factory()
          return value

以上是Python中defaultdict的詳解(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除