Python collection模塊

​ collections模塊主要實現了一些container datatypes(容器類型),作爲builtin容器類型dict、list、set和tuple等的一個補充,包括以下新定義的容器類型:

類型 用途
namedtuple() factory function for creating tuple subclasses with named fields
deque 雙端隊列,支持兩端快速出入隊列操作
ChainMap 將多個mapping 對象組合成一個mapping對象
Counter dict subclass,用來計數hashable對象
OrderedDict dict subclass that remembers the order entries were added
defaultdict dict subclass,對dict中missing的key,調用一個指定的factory function
UserDict wrapper around dictionary objects for easier dict subclassing
UserList wrapper around list objects for easier list subclassing
UserString wrapper around string objects for easier string subclassing

chainMap

chainMap(*maps)用於連接多個mapping對象稱爲一個mapping,通常其速度快於使用字典的update方法連接。其構造方法爲collections.ChainMap(*maps),當不傳入參數時,ChainMap則會產生一個空dict;ChainMap底層用list存儲mapping對象,並且可以通過.maps屬性對mapping對象進行修改,對mapping對象的修改能夠即時反應到ChainMap對象;查找會依次搜索每一個mapping對象,而修改、刪除、新增只會在第一個mapping對象上進行操作。

​ ChainMap對象除了支持所有的mapping對象方法,還有以下幾個特有的屬性、方法:

  • maps

    如上所述,返回ChainMap所包裝的mapping對象所組成的list,該list至少包含一個mapping對象;

  • new_child(m=None)

    創建child context,等價於ChainMap(m, *d.maps),當mNone時,等價於ChainMap({}, *d.maps)

  • parents

    返回parent context,即返回除了第一個mapping對象之外的所有mapping對象組成的ChainMap對象

    舉個簡單的例子:

    >>> baseline = {'music': 'bach', 'art': 'rembrandt'}                                                                
    >>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}                                                            
    >>> from collections import ChainMap                                                                                
    >>> a = ChainMap(adjustments, baseline) 
    >>> a['art']                                                                                                        
    'van gogh'
    

Counter

Counter([iterable-or-mapping])類用於快速、方便的計數,使用頻率很高的一個功能,它繼承於dict,用於計數可哈希的對象,計數的數值可以是正整數、負整數和零,實例化方法有以下幾種:

>>> c = Counter()                           # a new, empty counter
>>> c = Counter('gallahad')                 # a new counter from an iterable
>>> c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
>>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args

Counterdict不同之處在於如果Counter檢索一個不存在的key時不會拋出KeyError,而是返回0:

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']                              # count of a missing element is zero
0
>>> del c['eggs']

​ 除了支持父類dict大部分的方法(不支持fromkeys(iterable)以及update([*iterable-or-mapping*])略有不同),還包括以下幾個:

  • elements()

    返回一個iterator對象,每個元素重複其計數數值的次數(如果計數數值小於1,就忽略它)

    >>> c = Counter(a=4, b=2, c=-1, d=-2)
    >>> c.elements()
    >>> list(c.elements())  # If an element’s count is less than one, elements() will ignore it.
    
    <itertools.chain at 0x112f1be10>
    ['a', 'a', 'a', 'a', 'b', 'b']
    
  • most_common(n)

    返回計數最大的n個元素

    >>> c.most_common(2)
    c.most_common(2)
    
    >>> c.most_common()[:-3:-1] # c.most_common()[:-n-1:-1] n least common elements
    
  • subtract()

    執行計數減法操作,加法可以使用update()方法

    >>> c.subtract(Counter(a=1, b=2, c=3, d=4))
    >>> c
    >>> c.update({'a':3})  # update([iterable-or-mapping]), 添加式的update
    >>> c
    
    Counter({'a': 3, 'b': 0, 'c': -4, 'd': -6})
    Counter({'a': 6, 'b': 0, 'c': -4, 'd': -6})
    

    Counter還支持一些數學運算符,進行簡便的運算:

    >>> c = Counter(a=-3, b=5, c=4)
    >>> d = Counter(a=-1, b=2, c=0)
    >>> c + d                       # add two counters together:  c[x] + d[x] (keeping only positive counts)
    Counter({'b': 7, 'c': 4})
    >>> c - d                       # subtract (keeping only positive counts)
    Counter({'b': 3, 'c': 4})
    >>> c & d                       # intersection:  min(c[x], d[x]) (keeping only positive counts)
    Counter({'b': 2})
    >>> c | d                       # union:  max(c[x], d[x]) (keeping only positive counts)
    Counter({'b': 5, 'c': 4})
    
    # 特殊的是,還提供兩個shortcuts來表示空的Counter()加上或減去Counter對象
    >>> c = Counter(a=2, b=-4)
    >>> +c  # 相當於 Counter() + c
    Counter({'a': 2})
    >>> -c  # 相當於 Counter() - c
    Counter({'b': 4})
    

deque

collections.deque([iterable[, maxlen]]),以一個iterable對象作爲參數進行初始化,得到一個雙端隊列。雙端隊列支持從任一側線程安全,內存高效的appendpop操作,並且複雜度爲時間O(1)。如果未指定maxlen或爲None,則雙端隊列的長度可以增長到任意大小;反之,雙端隊列會限制爲指定的最大長度。一旦限定長度的雙端隊列達到最大長度時,若再插入新的item,則隊列另一端會自動丟棄相應數量的item。

  • 方法和屬性

    • append(x)、appendleft(x) 插入元素,前者插到隊列右邊,後者插入到左邊
    • extend(iterable)、extendleft(iterable) 插入多個元素,前者插到隊列右邊,後者插入到左邊
    • pop()、popleft() 去除並返回最左(右)邊的元素
    • clear() 清空隊列
    • copy() 隊列淺拷貝(同copy.copy(d)),深拷貝使用copy.deepcopy(d)
    • count(x) 計數隊列中與x大小相等的元素個數
    • index(x[, start[, stop]]) 在[start, stop)範圍內查找與x相等的第一個元素的index,如果沒找到拋出ValueErrorc錯誤
    • insert(i, x) 在位置i插入x,若i位置超出隊列指定的最大長度,引發IndexError
    • remove(value) 去除第一個與value相等的元素,若不存在引發ValueError
    • reverse() in place的反轉隊列元素
    • rotate(n=1) n> 0, 隊列向右移n步,反之向左移
    • maxlen,這是一個只讀屬性

    除了以上的屬性方法, deques 還支持iteration, pickling, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), in 操作符 以及索引操作,如d[-1]. 索引隊列兩邊的元素複雜度都是O(1),當索引獲取中間的元素複雜度會是O(n), 因此涉及大量中間元素索引操作的場景應該使用list。

  • 應用舉例

    比如獲取一個文件的後N行可以採用以下方式:

    def tail(filename, n=10):
        with open(filename) as f:
            return deque(f, n)
    

defaultdict

​ 關於defaultdict,我們可以先看一下dict中一個類似功能的函數setdefault(k, d=None):

d = {}
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]

for k, v in s:
    d.setdefault(k, []).append(v)

sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

​ 在上述代碼中,setdefault(k, d=[])一句話相當於做了兩件事情:一是獲取key爲k的的value,二是若該key不存在則設置其值爲[]。即,d.get(k, [])以及如果key不存在就設置d[k] = []。現在再回到defaultdictdefaultdict([default_factory[, ...]]),構造方法中default_factory默認是Nonedefaultdictdict的一個subclass,它重寫了一個__miss__方法以及擁有一個default_factory屬性。

  • __miss__(key)

    • 該方法在當key不存在時纔會且僅被__getitem__()調用,所以當使用dict.get()方法獲取一個不存在的鍵值時,返回結果爲None,而不是使用default_factory

    • default_factoryNone時,索引一個不存在的key會引發KeyError異常;

    • default_factory不爲None時,索引一個不存在的key則使用default_factory進行初始化並返回值

      >>> from collections import defaultdict
      
      >>> d2 = defaultdict(int ,a=1, b=2)
      >>> d2['c']
      0
      
      >>> d2 = defaultdict(int ,a=1, b=2)
      >>> d2.get('c')
      None
      >>> d2
      defaultdict(int, {'a': 1, 'b': 2})
      
      >>> d1 = defaultdict(a=1, b=2)
      >>> d1['c']
      KeyError: 'c'
      
  • default_factory

    該屬性在上述__miss__()函數中使用,由構造函數中的default_factory初始化,默認爲None

    ​ 因此,上述例子可以使用defaultdict來實現如下:

    s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
    d = defaultdict(list)
    for k, v in s:
        d[k].append(v)
    
    sorted(d.items())
    [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
    

namedtuple

namedtuple用來構造帶字段名的元組namedtuple的實例和普通元組消耗的內存一樣多,因爲字段名都被存在對應的類裏面。這個類跟普通的對象實例比起來也要小一些,因爲 Python 不會用 __dict__ 來存放這些實例的屬性。它們可以在使用常規tuple的任何地方使用,並且它們還具有按名稱(tuple只能按index)訪問字段的能力。其構造函數爲:collections.namedtuple(typename, field_names,*, rename=False, defaults=None, module=None),簡單例子如下:

>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

OrderedDict

OrderedDict和一般的dict類似,但是還具有記憶字典中存儲的key的插入順序。但是現在OrderDict可能用處不大了,因爲從Python 3.7開始,一般的dict也具有這個特性。但是,dictOrderedDict依舊有些區別:

  • 對於常規dict,最重要的高效的mapping operations,而Track 插入順序是次要的;
  • 對於OrderedDict,最重要的是排序操作,而空間效率、迭代效率以及更新字典的性能是次要的;
  • OrderedDict可以處理頻繁的需要重排的操作,適合於Track最近的操作;
  • 判斷兩個OrderedDict是否相等同時要求順序也相同;
  • OrderedDictpopitem(last=True)中可以通過last參數控制pop哪一端的元素;
  • OrderedDictmove_to_end(key, last=True)可以高效的將元素移至末尾;
  • 直到Python3.8,dict沒有__reversed__()方法;

UserDict & UserList & UserString

UserDict & UserList & UserString分別是dictliststr的一個wrapper,在實際應用中,已經很少使用到它們了,因爲我們可以直接方便的繼承它們,自定義對應的子類,稍微瞭解一下即可,下面給出一個簡單的例子:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章