collections --- 容器數(shù)據(jù)類型?

Source code: Lib/collections/__init__.py


這個模塊實(shí)現(xiàn)了特定目標(biāo)的容器,以提供Python標(biāo)準(zhǔn)內(nèi)建容器 dict , list , set , 和 tuple 的替代選擇。

namedtuple()

創(chuàng)建命名元組子類的工廠函數(shù)

deque

類似列表(list)的容器,實(shí)現(xiàn)了在兩端快速添加(append)和彈出(pop)

ChainMap

類似字典(dict)的容器類,將多個映射集合到一個視圖里面

Counter

字典的子類,提供了可哈希對象的計數(shù)功能

OrderedDict

字典的子類,保存了他們被添加的順序

defaultdict

字典的子類,提供了一個工廠函數(shù),為字典查詢提供一個默認(rèn)值

UserDict

封裝了字典對象,簡化了字典子類化

UserList

封裝了列表對象,簡化了列表子類化

UserString

封裝了字符串對象,簡化了字符串子類化

ChainMap 對象?

3.3 新版功能.

一個 ChainMap 類是為了將多個映射快速的鏈接到一起,這樣它們就可以作為一個單元處理。它通常比創(chuàng)建一個新字典和多次調(diào)用 update() 要快很多。

這個類可以用于模擬嵌套作用域,并且在模版化的時候比較有用。

class collections.ChainMap(*maps)?

一個 ChainMap 將多個字典或者其他映射組合在一起,創(chuàng)建一個單獨(dú)的可更新的視圖。 如果沒有 maps 被指定,就提供一個默認(rèn)的空字典,這樣一個新鏈至少有一個映射。

底層映射被存儲在一個列表中。這個列表是公開的,可以通過 maps 屬性存取和更新。沒有其他的狀態(tài)。

搜索查詢底層映射,直到一個鍵被找到。不同的是,寫,更新和刪除只操作第一個映射。

一個 ChainMap 通過引用合并底層映射。 所以,如果一個底層映射更新了,這些更改會反映到 ChainMap

支持所有常用字典方法。另外還有一個 maps 屬性(attribute),一個創(chuàng)建子上下文的方法(method), 一個存取它們首個映射的屬性(property):

maps?

一個可以更新的映射列表。這個列表是按照第一次搜索到最后一次搜索的順序組織的。它是僅有的存儲狀態(tài),可以被修改。列表最少包含一個映射。

new_child(m=None, **kwargs)?

返回一個新的 ChainMap,其中包含一個新的映射,后面跟隨當(dāng)前實(shí)例中的所有映射。 如果指定了 m,它會成為新的映射加在映射列表的前面;如果未指定,則會使用一個空字典,因此調(diào)用 d.new_child() 就等價于 ChainMap({}, *d.maps)。 如果指定了任何關(guān)鍵字參數(shù),它們會更新所傳入的映射或新的空字典。 此方法被用于創(chuàng)建子上下文,它可在不改變?nèi)魏紊霞売成涞那闆r下被更新。

在 3.4 版更改: 添加了 m 可選參數(shù)。

在 3.10 版更改: 增加了對關(guān)鍵字參數(shù)的支持。

parents?

屬性返回一個新的 ChainMap 包含所有的當(dāng)前實(shí)例的映射,除了第一個。這樣可以在搜索的時候跳過第一個映射。 使用的場景類似在 nested scopes 嵌套作用域中使用 nonlocal 關(guān)鍵詞。用例也可以類比內(nèi)建函數(shù) super() 。一個 d.parents 的引用等價于 ChainMap(*d.maps[1:]) 。

注意,一個 ChainMap() 的迭代順序是通過從后往前掃描所有映射來確定的:

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

這給出了與 dict.update() 調(diào)用序列相同的順序,從最后一個映射開始:

>>>
>>> combined = baseline.copy()
>>> combined.update(adjustments)
>>> list(combined)
['music', 'art', 'opera']

在 3.9 版更改: 增加了對 ||= 運(yùn)算符的支持,相關(guān)說明見 PEP 584

參見

ChainMap 例子和方法?

這一節(jié)提供了多個使用鏈映射的案例。

模擬Python內(nèi)部lookup鏈的例子

import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))

讓用戶指定的命令行參數(shù)優(yōu)先于環(huán)境變量,優(yōu)先于默認(rèn)值的例子

import os, argparse

defaults = {'color': 'red', 'user': 'guest'}

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}

combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])

ChainMap 類模擬嵌套上下文的例子

c = ChainMap()        # Create root context
d = c.new_child()     # Create nested child context
e = c.new_child()     # Child of c, independent from d
e.maps[0]             # Current context dictionary -- like Python's locals()
e.maps[-1]            # Root context -- like Python's globals()
e.parents             # Enclosing context chain -- like Python's nonlocals

d['x'] = 1            # Set value in current context
d['x']                # Get first key in the chain of contexts
del d['x']            # Delete from current context
list(d)               # All nested values
k in d                # Check all nested values
len(d)                # Number of nested values
d.items()             # All nested items
dict(d)               # Flatten into a regular dictionary

ChainMap 類只更新鏈中的第一個映射,但lookup會搜索整個鏈。 然而,如果需要深度寫和刪除,也可以很容易的通過定義一個子類來實(shí)現(xiàn)它

class DeepChainMap(ChainMap):
    'Variant of ChainMap that allows direct updates to inner scopes'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'         # update an existing key two levels down
>>> d['snake'] = 'red'           # new keys get added to the topmost dict
>>> del d['elephant']            # remove an existing key one level down
>>> d                            # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

Counter 對象?

一個計數(shù)器工具提供快速和方便的計數(shù)。比如

>>>
>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})

>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
 ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
class collections.Counter([iterable-or-mapping])?

一個 Counter 是一個 dict 的子類,用于計數(shù)可哈希對象。它是一個集合,元素像字典鍵(key)一樣存儲,它們的計數(shù)存儲為值。計數(shù)可以是任何整數(shù)值,包括0和負(fù)數(shù)。 Counter 類有點(diǎn)像其他語言中的 bags或multisets。

元素從一個 iterable 被計數(shù)或從其他的 mapping (or counter)初始化:

>>>
>>> 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

Counter對象有一個字典接口,如果引用的鍵沒有任何記錄,就返回一個0,而不是彈出一個 KeyError :

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

設(shè)置一個計數(shù)為0不會從計數(shù)器中移去一個元素。使用 del 來刪除它:

>>>
>>> c['sausage'] = 0                        # counter entry with a zero count
>>> del c['sausage']                        # del actually removes the entry

3.1 新版功能.

在 3.7 版更改: As a dict subclass, Counter inherited the capability to remember insertion order. Math operations on Counter objects also preserve order. Results are ordered according to when an element is first encountered in the left operand and then by the order encountered in the right operand.

Counter objects support additional methods beyond those available for all dictionaries:

elements()?

返回一個迭代器,其中每個元素將重復(fù)出現(xiàn)計數(shù)值所指定次。 元素會按首次出現(xiàn)的順序返回。 如果一個元素的計數(shù)值小于一,elements() 將會忽略它。

>>>
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common([n])?

返回一個列表,其中包含 n 個最常見的元素及出現(xiàn)次數(shù),按常見程度由高到低排序。 如果 n 被省略或?yàn)?Nonemost_common() 將返回計數(shù)器中的 所有 元素。 計數(shù)值相等的元素按首次出現(xiàn)的順序排序:

>>>
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
subtract([iterable-or-mapping])?

迭代對象映射對象 減去元素。像 dict.update() 但是是減去,而不是替換。輸入和輸出都可以是0或者負(fù)數(shù)。

>>>
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

3.2 新版功能.

total()?

計算總計數(shù)值。

>>>
>>> c = Counter(a=10, b=5, c=0)
>>> c.total()
15

3.10 新版功能.

通常字典方法都可用于 Counter 對象,除了有兩個方法工作方式與字典并不相同。

fromkeys(iterable)?

這個類方法沒有在 Counter 中實(shí)現(xiàn)。

update([iterable-or-mapping])?

迭代對象 計數(shù)元素或者 從另一個 映射對象 (或計數(shù)器) 添加。 像 dict.update() 但是是加上,而不是替換。另外,迭代對象 應(yīng)該是序列元素,而不是一個 (key, value) 對。

計數(shù)對象支持相等性、子集和超集關(guān)系等富比較運(yùn)算符: ==, !=, <, <=, >, >=。 所有這些檢測會將不存在的元素當(dāng)作計數(shù)值為零,因此 Counter(a=1) == Counter(a=1, b=0) 將返回真值。

3.10 新版功能: Rich comparison operations were added.

在 3.10 版更改: 在相等性檢測中,不存在的元素會被當(dāng)作計數(shù)值為零。 在此之前,Counter(a=3)Counter(a=3, b=0) 會被視為不同。

Counter 對象的常用案例

c.total()                       # total of all counts
c.clear()                       # reset all counts
list(c)                         # list unique elements
set(c)                          # convert to a set
dict(c)                         # convert to a regular dictionary
c.items()                       # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

Several mathematical operations are provided for combining Counter objects to produce multisets (counters that have counts greater than zero). Addition and subtraction combine counters by adding or subtracting the counts of corresponding elements. Intersection and union return the minimum and maximum of corresponding counts. Equality and inclusion compare corresponding counts. Each operation can accept inputs with signed counts, but the output will exclude results with counts of zero or less.

>>>
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # add two counters together:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})
>>> c == d                      # equality:  c[x] == d[x]
False
>>> c <= d                      # inclusion:  c[x] <= d[x]
False

單目加和減(一元操作符)意思是從空計數(shù)器加或者減去。

>>>
>>> c = Counter(a=2, b=-4)
>>> +c
Counter({'a': 2})
>>> -c
Counter({'b': 4})

3.3 新版功能: 添加了對一元加,一元減和位置集合操作的支持。

備注

計數(shù)器主要是為了表達(dá)運(yùn)行的正的計數(shù)而設(shè)計;但是,小心不要預(yù)先排除負(fù)數(shù)或者其他類型。為了幫助這些用例,這一節(jié)記錄了最小范圍和類型限制。

  • Counter 類是一個字典的子類,不限制鍵和值。值用于表示計數(shù),但你實(shí)際上 可以 存儲任何其他值。

  • most_common() 方法在值需要排序的時候用。

  • 原地操作比如 c[key] += 1 , 值類型只需要支持加和減。 所以分?jǐn)?shù),小數(shù),和十進(jìn)制都可以用,負(fù)值也可以支持。這兩個方法 update()subtract() 的輸入和輸出也一樣支持負(fù)數(shù)和0。

  • Multiset多集合方法只為正值的使用情況設(shè)計。輸入可以是負(fù)數(shù)或者0,但只輸出計數(shù)為正的值。沒有類型限制,但值類型需要支持加,減和比較操作。

  • elements() 方法要求正整數(shù)計數(shù)。忽略0和負(fù)數(shù)計數(shù)。

參見

  • Bag class 在 Smalltalk。

  • Wikipedia 鏈接 Multisets.

  • C++ multisets 教程和例子。

  • 數(shù)學(xué)操作和多集合用例,參考 Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19 。

  • 在給定數(shù)量和集合元素枚舉所有不同的多集合,參考 itertools.combinations_with_replacement()

    map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC
    

deque 對象?

class collections.deque([iterable[, maxlen]])?

返回一個新的雙向隊(duì)列對象,從左到右初始化(用方法 append()) ,從 iterable (迭代對象) 數(shù)據(jù)創(chuàng)建。如果 iterable 沒有指定,新隊(duì)列為空。

Deque隊(duì)列是由?;蛘遯ueue隊(duì)列生成的(發(fā)音是 “deck”,”double-ended queue”的簡稱)。Deque 支持線程安全,內(nèi)存高效添加(append)和彈出(pop),從兩端都可以,兩個方向的大概開銷都是 O(1) 復(fù)雜度。

雖然 list 對象也支持類似操作,不過這里優(yōu)化了定長操作和 pop(0)insert(0, v) 的開銷。它們引起 O(n) 內(nèi)存移動的操作,改變底層數(shù)據(jù)表達(dá)的大小和位置。

如果 maxlen 沒有指定或者是 None ,deques 可以增長到任意長度。否則,deque就限定到指定最大長度。一旦限定長度的deque滿了,當(dāng)新項(xiàng)加入時,同樣數(shù)量的項(xiàng)就從另一端彈出。限定長度deque提供類似Unix filter tail 的功能。它們同樣可以用與追蹤最近的交換和其他數(shù)據(jù)池活動。

雙向隊(duì)列(deque)對象支持以下方法:

append(x)?

添加 x 到右端。

appendleft(x)?

添加 x 到左端。

clear()?

移除所有元素,使其長度為0.

copy()?

創(chuàng)建一份淺拷貝。

3.5 新版功能.

count(x)?

計算 deque 中元素等于 x 的個數(shù)。

3.2 新版功能.

extend(iterable)?

擴(kuò)展deque的右側(cè),通過添加iterable參數(shù)中的元素。

extendleft(iterable)?

擴(kuò)展deque的左側(cè),通過添加iterable參數(shù)中的元素。注意,左添加時,在結(jié)果中iterable參數(shù)中的順序?qū)⒈环催^來添加。

index(x[, start[, stop]])?

返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一個匹配項(xiàng),如果未找到則引發(fā) ValueError。

3.5 新版功能.

insert(i, x)?

在位置 i 插入 x 。

如果插入會導(dǎo)致一個限長 deque 超出長度 maxlen 的話,就引發(fā)一個 IndexError。

3.5 新版功能.

pop()?

移去并且返回一個元素,deque 最右側(cè)的那一個。 如果沒有元素的話,就引發(fā)一個 IndexError

popleft()?

移去并且返回一個元素,deque 最左側(cè)的那一個。 如果沒有元素的話,就引發(fā) IndexError。

remove(value)?

移除找到的第一個 value。 如果沒有的話就引發(fā) ValueError

reverse()?

將deque逆序排列。返回 None 。

3.2 新版功能.

rotate(n=1)?

向右循環(huán)移動 n 步。 如果 n 是負(fù)數(shù),就向左循環(huán)。

如果deque不是空的,向右循環(huán)移動一步就等價于 d.appendleft(d.pop()) , 向左循環(huán)一步就等價于 d.append(d.popleft())

Deque對象同樣提供了一個只讀屬性:

maxlen?

Deque的最大尺寸,如果沒有限定的話就是 None 。

3.1 新版功能.

除了以上操作,deque 還支持迭代、封存、len(d)、reversed(d)、copy.copy(d)copy.deepcopy(d)、成員檢測運(yùn)算符 in 以及下標(biāo)引用例如通過 d[0] 訪問首個元素等。 索引訪問在兩端的復(fù)雜度均為 O(1) 但在中間則會低至 O(n)。 如需快速隨機(jī)訪問,請改用列表。

Deque從版本3.5開始支持 __add__(), __mul__(), 和 __imul__() 。

示例:

>>>
>>> from collections import deque
>>> d = deque('ghi')                 # make a new deque with three items
>>> for elem in d:                   # iterate over the deque's elements
...     print(elem.upper())
G
H
I

>>> d.append('j')                    # add a new entry to the right side
>>> d.appendleft('f')                # add a new entry to the left side
>>> d                                # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])

>>> d.pop()                          # return and remove the rightmost item
'j'
>>> d.popleft()                      # return and remove the leftmost item
'f'
>>> list(d)                          # list the contents of the deque
['g', 'h', 'i']
>>> d[0]                             # peek at leftmost item
'g'
>>> d[-1]                            # peek at rightmost item
'i'

>>> list(reversed(d))                # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d                         # search the deque
True
>>> d.extend('jkl')                  # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)                      # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)                     # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])

>>> deque(reversed(d))               # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()                        # empty the deque
>>> d.pop()                          # cannot pop from an empty deque
Traceback (most recent call last):
    File "<pyshell#6>", line 1, in -toplevel-
        d.pop()
IndexError: pop from an empty deque

>>> d.extendleft('abc')              # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])

deque 用法?

這一節(jié)展示了deque的多種用法。

限長deque提供了類似Unix tail 過濾功能

def tail(filename, n=10):
    'Return the last n lines of a file'
    with open(filename) as f:
        return deque(f, n)

另一個用法是維護(hù)一個近期添加元素的序列,通過從右邊添加和從左邊彈出

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

一個 輪詢調(diào)度器 可以通過在 deque 中放入迭代器來實(shí)現(xiàn)。值從當(dāng)前迭代器的位置0被取出并暫存(yield)。 如果這個迭代器消耗完畢,就用 popleft() 將其從對列中移去;否則,就通過 rotate() 將它移到隊(duì)列的末尾

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    iterators = deque(map(iter, iterables))
    while iterators:
        try:
            while True:
                yield next(iterators[0])
                iterators.rotate(-1)
        except StopIteration:
            # Remove an exhausted iterator.
            iterators.popleft()

rotate() 方法提供了一種方式來實(shí)現(xiàn) deque 切片和刪除。 例如, 一個純的Python del d[n] 實(shí)現(xiàn)依賴于 rotate() 來定位要彈出的元素

def delete_nth(d, n):
    d.rotate(-n)
    d.popleft()
    d.rotate(n)

要實(shí)現(xiàn) deque 切片, 使用一個類似的方法,應(yīng)用 rotate() 將目標(biāo)元素放到左邊。通過 popleft() 移去老的條目(entries),通過 extend() 添加新的條目, 然后反向 rotate。這個方法可以最小代價實(shí)現(xiàn)命令式的棧操作,諸如 dup, drop, swap, over, pick, rot, 和 roll 。

defaultdict 對象?

class collections.defaultdict(default_factory=None, /[, ...])?

返回一個新的類似字典的對象。 defaultdict 是內(nèi)置 dict 類的子類。 它重載了一個方法并添加了一個可寫的實(shí)例變量。 其余的功能與 dict 類相同因而不在此文檔中寫明。

本對象包含一個名為 default_factory 的屬性,構(gòu)造時,第一個參數(shù)用于為該屬性提供初始值,默認(rèn)為 None。所有其他參數(shù)(包括關(guān)鍵字參數(shù))都相當(dāng)于傳遞給 dict 的構(gòu)造函數(shù)。

defaultdict 對象除了支持標(biāo)準(zhǔn) dict 的操作,還支持以下方法作為擴(kuò)展:

__missing__(key)?

如果 default_factory 屬性為 None,則調(diào)用本方法會拋出 KeyError 異常,附帶參數(shù) key。

如果 default_factory 不為 None,則它會被(不帶參數(shù)地)調(diào)用來為 key 提供一個默認(rèn)值,這個值和 key 作為一對鍵值對被插入到字典中,并作為本方法的返回值返回。

如果調(diào)用 default_factory 時拋出了異常,這個異常會原封不動地向外層傳遞。

在無法找到所需鍵值時,本方法會被 dict 中的 __getitem__() 方法調(diào)用。無論本方法返回了值還是拋出了異常,都會被 __getitem__() 傳遞。

注意,__missing__() 不會__getitem__() 以外的其他方法調(diào)用。意味著 get() 會像正常的 dict 那樣返回 None,而不是使用 default_factory。

defaultdict 對象支持以下實(shí)例變量:

default_factory?

本屬性由 __missing__() 方法來調(diào)用。如果構(gòu)造對象時提供了第一個參數(shù),則本屬性會被初始化成那個參數(shù),如果未提供第一個參數(shù),則本屬性為 None

在 3.9 版更改: 增加了合并 (|) 與更新 (|=) 運(yùn)算符,相關(guān)說明見 PEP 584

defaultdict 例子?

使用 list 作為 default_factory,很輕松地將(鍵-值對組成的)序列轉(zhuǎn)換為(鍵-列表組成的)字典:

>>>
>>> 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])]

當(dāng)每個鍵第一次遇見時,它還沒有在字典里面,所以自動創(chuàng)建該條目,即調(diào)用 default_factory 方法,返回一個空的 list。 list.append() 操作添加值到這個新的列表里。當(dāng)再次存取該鍵時,就正常操作,list.append() 添加另一個值到列表中。這個計數(shù)比它的等價方法 dict.setdefault() 要快速和簡單:

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

設(shè)置 default_factoryint,使 defaultdict 用于計數(shù)(類似其他語言中的 bag 或 multiset):

>>>
>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]

當(dāng)一個字母首次遇到時,它會查詢失敗,則 default_factory 會調(diào)用 int() 來提供一個整數(shù) 0 作為默認(rèn)值。后續(xù)的自增操作建立起對每個字母的計數(shù)。

函數(shù) int() 總是返回 0,這是常數(shù)函數(shù)的特殊情況。一個更快和靈活的方法是使用 lambda 函數(shù),可以提供任何常量值(不只是0):

>>>
>>> def constant_factory(value):
...     return lambda: value
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'

設(shè)置 default_factoryset 使 defaultdict 用于構(gòu)建 set 集合:

>>>
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
...     d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]

namedtuple() 命名元組的工廠函數(shù)?

命名元組賦予每個位置一個含義,提供可讀性和自文檔性。它們可以用于任何普通元組,并添加了通過名字獲取值的能力,通過索引值也是可以的。

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)?

返回一個新的元組子類,名為 typename 。這個新的子類用于創(chuàng)建類元組的對象,可以通過字段名來獲取屬性值,同樣也可以通過索引和迭代獲取值。子類實(shí)例同樣有文檔字符串(類名和字段名)另外一個有用的 __repr__() 方法,以 name=value 格式列明了元組內(nèi)容。

field_names 是一個像 [‘x’, ‘y’] 一樣的字符串序列。另外 field_names 可以是一個純字符串,用空白或逗號分隔開元素名,比如 'x y' 或者 'x, y' 。

任何有效的Python 標(biāo)識符都可以作為字段名,除了下劃線開頭的那些。有效標(biāo)識符由字母,數(shù)字,下劃線組成,但首字母不能是數(shù)字或下劃線,另外不能是關(guān)鍵詞 keyword 比如 class, for, return, global, pass, 或 raise 。

如果 rename 為真, 無效字段名會自動轉(zhuǎn)換成位置名。比如 ['abc', 'def', 'ghi', 'abc'] 轉(zhuǎn)換成 ['abc', '_1', 'ghi', '_3'] , 消除關(guān)鍵詞 def 和重復(fù)字段名 abc

defaults 可以為 None 或者是一個默認(rèn)值的 iterable 。如果一個默認(rèn)值域必須跟其他沒有默認(rèn)值的域在一起出現(xiàn),defaults 就應(yīng)用到最右邊的參數(shù)。比如如果域名 ['x', 'y', 'z'] 和默認(rèn)值 (1, 2) ,那么 x 就必須指定一個參數(shù)值 ,y 默認(rèn)值 1 , z 默認(rèn)值 2 。

如果 module 值有定義,命名元組的 __module__ 屬性值就被設(shè)置。

命名元組實(shí)例沒有字典,所以它們要更輕量,并且占用更小內(nèi)存。

要支持封存操作,應(yīng)當(dāng)將命名元組類賦值給一個匹配 typename 的變量。

在 3.1 版更改: 添加了對 rename 的支持。

在 3.6 版更改: verboserename 參數(shù)成為 僅限關(guān)鍵字參數(shù).

在 3.6 版更改: 添加了 module 參數(shù)。

在 3.7 版更改: 移除了 verbose 形參和 _source 屬性。

在 3.7 版更改: 添加了 defaults 參數(shù)和 _field_defaults 屬性。

>>>
>>> # 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)

命名元組尤其有用于賦值 csv sqlite3 模塊返回的元組

EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
    print(emp.name, emp.title)

import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
    print(emp.name, emp.title)

除了繼承元組的方法,命名元組還支持三個額外的方法和兩個屬性。為了防止字段名沖突,方法和屬性以下劃線開始。

classmethod somenamedtuple._make(iterable)?

類方法從存在的序列或迭代實(shí)例創(chuàng)建一個新實(shí)例。

>>>
>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
somenamedtuple._asdict()?

返回一個新的 dict ,它將字段名稱映射到它們對應(yīng)的值:

>>>
>>> p = Point(x=11, y=22)
>>> p._asdict()
{'x': 11, 'y': 22}

在 3.1 版更改: 返回一個 OrderedDict 而不是 dict 。

在 3.8 版更改: 返回一個常規(guī) dict 而不是 OrderedDict。 因?yàn)樽?Python 3.7 起,常規(guī)字典已經(jīng)保證有序。 如果需要 OrderedDict 的額外特性,推薦的解決方案是將結(jié)果轉(zhuǎn)換為需要的類型: OrderedDict(nt._asdict())。

somenamedtuple._replace(**kwargs)?

返回一個新的命名元組實(shí)例,并將指定域替換為新的值

>>>
>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)

>>> for partnum, record in inventory.items():
...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
somenamedtuple._fields?

字符串元組列出了字段名。用于提醒和從現(xiàn)有元組創(chuàng)建一個新的命名元組類型。

>>>
>>> p._fields            # view the field names
('x', 'y')

>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)
somenamedtuple._field_defaults?

字典將字段名稱映射到默認(rèn)值。

>>>
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
>>> Account._field_defaults
{'balance': 0}
>>> Account('premium')
Account(type='premium', balance=0)

要獲取這個名字域的值,使用 getattr() 函數(shù) :

>>>
>>> getattr(p, 'x')
11

轉(zhuǎn)換一個字典到命名元組,使用 ** 兩星操作符 (所述如 解包實(shí)參列表):

>>>
>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)

因?yàn)橐粋€命名元組是一個正常的Python類,它可以很容易的通過子類更改功能。這里是如何添加一個計算域和定寬輸出打印格式:

>>>
>>> class Point(namedtuple('Point', ['x', 'y'])):
...     __slots__ = ()
...     @property
...     def hypot(self):
...         return (self.x ** 2 + self.y ** 2) ** 0.5
...     def __str__(self):
...         return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

>>> for p in Point(3, 4), Point(14, 5/7):
...     print(p)
Point: x= 3.000  y= 4.000  hypot= 5.000
Point: x=14.000  y= 0.714  hypot=14.018

上面的子類設(shè)置 __slots__ 為一個空元組。通過阻止創(chuàng)建實(shí)例字典保持了較低的內(nèi)存開銷。

子類化對于添加和存儲新的名字域是無效的。應(yīng)當(dāng)通過 _fields 創(chuàng)建一個新的命名元組來實(shí)現(xiàn)它:

>>>
>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))

文檔字符串可以自定義,通過直接賦值給 __doc__ 屬性:

>>>
>>> Book = namedtuple('Book', ['id', 'title', 'authors'])
>>> Book.__doc__ += ': Hardcover book in active collection'
>>> Book.id.__doc__ = '13-digit ISBN'
>>> Book.title.__doc__ = 'Title of first printing'
>>> Book.authors.__doc__ = 'List of authors sorted by last name'

在 3.5 版更改: 文檔字符串屬性變成可寫。

參見

  • 請參閱 typing.NamedTuple ,以獲取為命名元組添加類型提示的方法。 它還使用 class 關(guān)鍵字提供了一種優(yōu)雅的符號:

    class Component(NamedTuple):
        part_number: int
        weight: float
        description: Optional[str] = None
    
  • 對于以字典為底層的可變域名, 參考 types.SimpleNamespace() 。

  • dataclasses 模塊提供了一個裝飾器和一些函數(shù),用于自動將生成的特殊方法添加到用戶定義的類中。

OrderedDict 對象?

有序詞典就像常規(guī)詞典一樣,但有一些與排序操作相關(guān)的額外功能。由于內(nèi)置的 dict 類獲得了記住插入順序的能力(在 Python 3.7 中保證了這種新行為),它們變得不那么重要了。

一些與 dict 的不同仍然存在:

  • 常規(guī)的 dict 被設(shè)計為非常擅長映射操作。 跟蹤插入順序是次要的。

  • OrderedDict 旨在擅長重新排序操作。 空間效率、迭代速度和更新操作的性能是次要的。

  • The OrderedDict algorithm can handle frequent reordering operations better than dict. As shown in the recipes below, this makes it suitable for implementing various kinds of LRU caches.

  • 對于 OrderedDict ,相等操作檢查匹配順序。

    A regular dict can emulate the order sensitive equality test with p == q and all(k1 == k2 for k1, k2 in zip(p, q)).

  • OrderedDict 類的 popitem() 方法有不同的簽名。它接受一個可選參數(shù)來指定彈出哪個元素。

    A regular dict can emulate OrderedDict's od.popitem(last=True) with d.popitem() which is guaranteed to pop the rightmost (last) item.

    A regular dict can emulate OrderedDict's od.popitem(last=False) with (k := next(iter(d)), d.pop(k)) which will return and remove the leftmost (first) item if it exists.

  • OrderedDict 類有一個 move_to_end() 方法,可以有效地將元素移動到任一端。

    A regular dict can emulate OrderedDict's od.move_to_end(k, last=True) with d[k] = d.pop(k) which will move the key and its associated value to the rightmost (last) position.

    A regular dict does not have an efficient equivalent for OrderedDict's od.move_to_end(k, last=False) which moves the key and its associated value to the leftmost (first) position.

  • Python 3.8之前, dict 缺少 __reversed__() 方法。

class collections.OrderedDict([items])?

返回一個 dict 子類的實(shí)例,它具有專門用于重新排列字典順序的方法。

3.1 新版功能.

popitem(last=True)?

有序字典的 popitem() 方法移除并返回一個 (key, value) 鍵值對。 如果 last 值為真,則按 LIFO 后進(jìn)先出的順序返回鍵值對,否則就按 FIFO 先進(jìn)先出的順序返回鍵值對。

move_to_end(key, last=True)?

Move an existing key to either end of an ordered dictionary. The item is moved to the right end if last is true (the default) or to the beginning if last is false. Raises KeyError if the key does not exist:

>>>
>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d)
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d)
'bacde'

3.2 新版功能.

相對于通常的映射方法,有序字典還另外提供了逆序迭代的支持,通過 reversed() 。

OrderedDict 之間的相等測試是順序敏感的,實(shí)現(xiàn)為 list(od1.items())==list(od2.items())OrderedDict 對象和其他的 Mapping 的相等測試,是順序敏感的字典測試。這允許 OrderedDict 替換為任何字典可以使用的場所。

在 3.5 版更改: OrderedDict 的項(xiàng)(item),鍵(key)和值(value) 視圖 現(xiàn)在支持逆序迭代,通過 reversed() 。

在 3.6 版更改: PEP 468 贊成將關(guān)鍵詞參數(shù)的順序保留, 通過傳遞給 OrderedDict 構(gòu)造器和它的 update() 方法。

在 3.9 版更改: 增加了合并 (|) 與更新 (|=) 運(yùn)算符,相關(guān)說明見 PEP 584。

OrderedDict 例子和用法?

創(chuàng)建記住鍵值 最后 插入順序的有序字典變體很簡單。 如果新條目覆蓋現(xiàn)有條目,則原始插入位置將更改并移至末尾:

class LastUpdatedOrderedDict(OrderedDict):
    'Store items in the order the keys were last added'

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.move_to_end(key)

一個 OrderedDict 對于實(shí)現(xiàn) functools.lru_cache() 的變體也很有用:

from time import time

class TimeBoundedLRU:
    "LRU Cache that invalidates and refreshes old entries."

    def __init__(self, func, maxsize=128, maxage=30):
        self.cache = OrderedDict()      # { args : (timestamp, result)}
        self.func = func
        self.maxsize = maxsize
        self.maxage = maxage

    def __call__(self, *args):
        if args in self.cache:
            self.cache.move_to_end(args)
            timestamp, result = self.cache[args]
            if time() - timestamp <= self.maxage:
                return result
        result = self.func(*args)
        self.cache[args] = time(), result
        if len(self.cache) > self.maxsize:
            self.cache.popitem(0)
        return result
class MultiHitLRUCache:
    """ LRU cache that defers caching a result until
        it has been requested multiple times.

        To avoid flushing the LRU cache with one-time requests,
        we don't cache until a request has been made more than once.

    """

    def __init__(self, func, maxsize=128, maxrequests=4096, cache_after=1):
        self.requests = OrderedDict()   # { uncached_key : request_count }
        self.cache = OrderedDict()      # { cached_key : function_result }
        self.func = func
        self.maxrequests = maxrequests  # max number of uncached requests
        self.maxsize = maxsize          # max number of stored return values
        self.cache_after = cache_after

    def __call__(self, *args):
        if args in self.cache:
            self.cache.move_to_end(args)
            return self.cache[args]
        result = self.func(*args)
        self.requests[args] = self.requests.get(args, 0) + 1
        if self.requests[args] <= self.cache_after:
            self.requests.move_to_end(args)
            if len(self.requests) > self.maxrequests:
                self.requests.popitem(0)
        else:
            self.requests.pop(args, None)
            self.cache[args] = result
            if len(self.cache) > self.maxsize:
                self.cache.popitem(0)
        return result

UserDict 對象?

UserDict 類是用作字典對象的外包裝。對這個類的需求已部分由直接創(chuàng)建 dict 的子類的功能所替代;不過,這個類處理起來更容易,因?yàn)榈讓拥淖值淇梢宰鳛閷傩詠碓L問。

class collections.UserDict([initialdata])?

模擬字典的類。 這個實(shí)例的內(nèi)容保存在一個常規(guī)字典中,它可以通過 UserDict 實(shí)例的 data 屬性來訪問。 如果提供了 initialdata,則 data 會用其內(nèi)容來初始化;請注意對 initialdata 的引用將不會被保留,以允許它被用于其他目的。

UserDict 實(shí)例提供了以下屬性作為擴(kuò)展方法和操作的支持:

data?

一個真實(shí)的字典,用于保存 UserDict 類的內(nèi)容。

UserList 對象?

這個類封裝了列表對象。它是一個有用的基礎(chǔ)類,對于你想自定義的類似列表的類,可以繼承和覆蓋現(xiàn)有的方法,也可以添加新的方法。這樣我們可以對列表添加新的行為。

對這個類的需求已部分由直接創(chuàng)建 list 的子類的功能所替代;不過,這個類處理起來更容易,因?yàn)榈讓拥牧斜砜梢宰鳛閷傩詠碓L問。

class collections.UserList([list])?

模擬一個列表。這個實(shí)例的內(nèi)容被保存為一個正常列表,通過 UserListdata 屬性存取。實(shí)例內(nèi)容被初始化為一個 list 的copy,默認(rèn)為 [] 空列表。 list 可以是迭代對象,比如一個Python列表,或者一個 UserList 對象。

UserList 提供了以下屬性作為可變序列的方法和操作的擴(kuò)展:

data?

一個 list 對象用于存儲 UserList 的內(nèi)容。

子類化的要求: UserList 的子類需要提供一個構(gòu)造器,可以無參數(shù)調(diào)用,或者一個參數(shù)調(diào)用。返回一個新序列的列表操作需要創(chuàng)建一個實(shí)現(xiàn)類的實(shí)例。它假定了構(gòu)造器可以以一個參數(shù)進(jìn)行調(diào)用,這個參數(shù)是一個序列對象,作為數(shù)據(jù)源。

如果一個分離的類不希望依照這個需求,所有的特殊方法就必須重寫;請參照源代碼進(jìn)行修改。

UserString 對象?

UserString 類是用作字符串對象的外包裝。對這個類的需求已部分由直接創(chuàng)建 str 的子類的功能所替代;不過,這個類處理起來更容易,因?yàn)榈讓拥淖址梢宰鳛閷傩詠碓L問。

class collections.UserString(seq)?

模擬一個字符串對象。這個實(shí)例對象的內(nèi)容保存為一個正常字符串,通過 UserStringdata 屬性存取。實(shí)例內(nèi)容初始化設(shè)置為 seq 的copy。seq 參數(shù)可以是任何可通過內(nèi)建 str() 函數(shù)轉(zhuǎn)換為字符串的對象。

UserString 提供了以下屬性作為字符串方法和操作的額外支持:

data?

一個真正的 str 對象用來存放 UserString 類的內(nèi)容。

在 3.5 版更改: 新方法 __getnewargs__, __rmod__, casefold, format_map, isprintable, 和 maketrans。