Python 3.7.1 模块 collections

19 篇文章 1 订阅
11 篇文章 0 订阅

源代码: Lib / collections / __ init__.py

另注:8.9.10部分翻译的比较水,找时间再重新研究一下

完…


1. 概览

这个模块实现专门的容器数据类型提供替代Python的通用内置容器 dict,list, set,和tuple。

namedtuple()用于创建具有命名字段的元组子类的工厂函数
deque类似列表的容器,两端都有快速追加和弹出
ChainMap类似于dict的类,用于创建多个映射的单个视图
Counter用于计算可哈希对象的dict子类
OrderedDict记住元素添加顺序的dict子类
defaultdictdict子类调用工厂函数来提供缺失值
UserDict包装字典对象以便于dict子类化
UserList包装列表对象以便于列表子类化
UserString包装字符串对象以便于字符串子类化

在版本3.3中更改:将集合抽象基类( Collections Abstract Base Classes)移动到 collections.abc 模块。为了向后兼容,它们将继续通过Python 3.7在此模块中可见。随后,他们将完全删除。

2. ChainMap 对象

版本 3.3 中的新功能。

ChainMap类用于快速链接多个映射,以便将它们视为一个单元。它通常比创建新字典和多次调用update()快得多。

该类可用于模拟嵌套作用域,在模板中很有用

2.1 class collections.ChainMap(*maps)

ChainMap将多个字典或其他映射组合在一起以创建单个可更新视图。如果未指定maps,则提供单个空字典,以便新链始终至少具有一个映射。

底层映射存储在列表中。该列表是公共的,可以使用maps属性访问或更新。没有其他声明。

查找会连续搜索映射,直到找到key。相反,写入,更新和删除仅在第一个映射上运行。

ChainMap通过引用合并了底层映射。因此,如果其中一个底层映射得到更新,那么这些更改将反映在ChainMap中。

译者实例
注意d和e,都存储在ChainMap中,但是搜索的时候从左到右,先到先得。

def collection_test2():
    import builtins
    from collections import ChainMap
    a = {"name": "leng"}
    b = {"age": 24}
    c = {"wife": "qian"}
    pylookup = ChainMap(a,b,c)
    print(pylookup)
    print(pylookup['age'],pylookup.maps)
    pylookup.update({"age": 25})
    print(pylookup)
    b['age'] = 26
    print(pylookup)
    print(type(pylookup.maps))
    pylookup.maps[0]['age']=20
    pylookup.maps[1]['age']=22
    print(pylookup)
    print("-----------")
    d = {"name": "leng"}
    e = {"name":"123"}
    cm = ChainMap(d,e)
    print(cm)
    print(cm['name'])
collection_test2()
#输出结果
ChainMap({'name': 'leng'}, {'age': 24}, {'wife': 'qian'})
24 [{'name': 'leng'}, {'age': 24}, {'wife': 'qian'}]
ChainMap({'name': 'leng', 'age': 25}, {'age': 24}, {'wife': 'qian'})
ChainMap({'name': 'leng', 'age': 25}, {'age': 26}, {'wife': 'qian'})
<class 'list'>
ChainMap({'name': 'leng', 'age': 20}, {'age': 22}, {'wife': 'qian'})
-----------
ChainMap({'name': 'leng'}, {'name': '123'})
leng

支持所有常用的字典方法。此外,还有一个 maps属性,一个用于创建新子上下文的方法,以及一个用于访问除第一个映射之外的所有映射的属性:

maps

用户可更新的映射列表。该列表从首次搜索到最后搜索排序。它是唯一存储的状态,可以进行修改以更改搜索的映射。该列表应始终包含至少一个映射。

new_child(m=None)

返回包含新映射的ChainMap,后跟当前实例中的所有映射。如果指定m,则它将成为映射列表最前面的新映射; 如果未指定,则使用空的dict。所以d.new_child()等效于ChainMap({}, *d.maps)。此方法用于创建可在不更改任何父映射中的值的情况下更新的子上下文。

版本3.4中已更改:添加了可选参数m

parents

返回新的ChainMap,包含当前实例中除第一个之外的所有映射。这对于在搜索中跳过第一个映射很有用。用例类似于嵌套作用域中使用的nonlocal关键字。用例也与内置函数super()的用法相同 。引用d.parents相当于ChainMap(*d.maps[1:])

译者实例

def collection_test3():
    import builtins
    from collections import ChainMap
    a = {"name": "leng","age": 20}
    b = {"age": 24}
    c = {"wife": "qian"}
    cm = ChainMap(a,b,c)
    nc1 = cm.new_child()
    nc2 = cm.new_child(m=b)
    print(nc1,nc2,sep='\n')
    print("_________")
    print(nc2.parents,nc1.parents,sep='\n')
collection_test3()
#输出结果
ChainMap({}, {'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
ChainMap({'age': 24}, {'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
_________
ChainMap({'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
ChainMap({'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})

也可以看看
Enthought的 CodeTools包中的MultiContext类具有支持写入链中任何映射的选项。
Django 用于模板化的Context类是一个只读的映射链。它还具有类似于new_child()方法和 parents属性的上下文推送和弹出功能 。

嵌套上下文方法具有选项来控制写入和其它突变是否只适用于第一映射或链中的任何映射。
一个大大简化的Chainmap只读版本

2.2 实际用途

本节介绍使用ChainMap的各种方法。

(1)模拟Python内部查找链的示例:

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

(2)让用户指定的命令行参数优先于环境变量的示例,而环境变量优先于默认值:

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}

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

(3)使用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']                # Get first key in the chain of contexts
d['x'] = 1            # Set value in current context
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

(4)如果需要对ChainMap进行深度写入和删除,则很容易创建一个子类来更新链中更深层次的键:

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'})

3. Counter 对象

支持方便和快速的计数。例如:

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

3.1 class collections.Counter([iterable-or-mapping])

Counter是用于计算可哈希对象的dict的子类。它是一个集合,其中元素存储为字典键,它们的计数存储为字典值。计数允许为任何整数值,包括零或负计数。Counter类是类似于其他语言中的包或者multisets 。

从iterable或从另一个映射(或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对象具有字典接口,一个不同之处是,它们为缺少的项返回零计数而不是报出KeyError

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

将计数设置为零不会从计数器中删除元素。用于del完全删除它:
版本3.1中的新功能。

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

Counter对象支持除可用于所有字典的方法之外的三种方法:

elements()

返回一个迭代器,重复每个元素的对应次。元素以任意顺序返回。如果元素的计数小于1,则忽略它。

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

most_common([n])

返回n个最常见元素及其计数的列表,从最常见到最少。如果省略n或者None,则返回计数器中的所有元素。具有相同计数的元素是任意排序的:

>>> Counter('abracadabra').most_common(3)  # doctest: +SKIP
[('a', 5), ('r', 2), ('b', 2)]

subtract([iterable-or-mapping])

从iterable或从另一个映射 (或counter)中减去元素。很像 dict.update() ,但是只减去计数而不是替换它们。输入和输出都可以为零或负数。

>>> 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中的新功能。

通常的字典方法可用于Counter对象,除了两个方法。

fromkeys(iterable)

Counter对象没有实现此类方法。

update([iterable-or-mapping])

元素从iterable或从另一个映射 (或counter)中添加。很像dict.update(),但是添加计数而不是替换它们。此外,iterable应该是是元素序列,而不是对序列(key, value)

3.2 常用操作

使用Counter对象的常见模式:

sum(c.values())                 # 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

在这里插入图片描述
提供了几种数学运算来组合Counter 对象以产生multisets (计数大于零的计数器)。加法和减法通过添加或减去相应元素的计数来组合计数器。&|分别返回相应计数的最小值和最大值。每个操作都可以接受带有符号计数的输入,但输出将排除计数为零或更少的结果。

>>> 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]) # doctest: +SKIP
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

一元加法和减法是用于添加空计数器或从空计数器中减去的快捷方式。

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

版本3.3中的新功能:添加了对一元加,一元减号和 in-place multiset 操作的支持。

注意
Counter主要用于处理正整数以表示运行计数; 但是,注意不要不必要地排除需要其他类型或负值的用例。为帮助处理这些用例,本节介绍了最小范围和类型限制。
① 在Counter类本身是字典的子类,其键和值没有任何限制。这些值旨在表示计数,但您可以在值字段中存储任何内容。
② most_common()方法仅需要值是可排序的。
③ 对于原位操作(in-place operations ),例如c[key] += 1,值类型只需要支持加法和减法。因此,分数,浮点数和小数将起作用,并且支持负值。update()和subtract()同样也是如此 ,并允许输入和输出有0或负值。
④ multiset方法仅适用于具有正值的用例。输入可以是负数或零,但仅创建具有正值的输出。没有类型限制,但值类型需要支持加法,减法和比较。
⑤ elements()方法需要整数计数。它忽略了零和负数。

也可以看看
Smalltalk中的 Bag类
维基百科的Multisets
带有示例的 C++ multisets
有关多Multisets及其用例的数学运算,请参阅<< Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19 >>。
要枚举给定元素集上给定大小的所有不同多重集,请参阅itertools.combinations_with_replacement()
map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC

4.deque对象

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

返回一个从左到右(使用append())初始化的新deque对象,其中包含来自iterable的数据。如果未指定iterable,则新的deque为空。

Deques是堆栈和队列的归纳(名称发音为“deck”,是“双端队列”的缩写)。Deques支持线程安全,内存有效的附加和从双端队列的弹出,在任一方向上具有大致相同的O(1)性能。

虽然list对象支持类似的操作,但它们针对快速固定长度操作进行了优化,并且导致pop(0)和insert(0, v)操作上的内存移动成本为O(n),这些操作改变了底层数据表示的大小和位置。

如果未指定maxlenNone,则deques可能会增长到任意长度。否则,双端队列限制为指定的最大长度。一旦有界长度双端队列已满,当添加新项目时,从另一端丢弃相应数量的项目。有界长度的deques提供类似于Unix中的tail过滤器的功能。它们还可用于跟踪仅涉及最近活动的事务和其他数据池。

4.2 方法

append(x)

将x添加到双端队列的右侧。
在这里插入图片描述

appendleft(x)

将x添加到双端队列的左侧。
在这里插入图片描述

clear()

从双端队列中删除所有元素,使其长度为0。
在这里插入图片描述

copy()

创建deque的浅拷贝。

版本3.5中的新功能。
在这里插入图片描述

count(x)

计算deque中元素等于x的数量。

版本3.2中的新功能。
在这里插入图片描述

extend(iterable)

通过附加可迭代参数中的元素来扩展双端队列的右侧。

extendleft(iterable)

通过附加来自iterable的元素来扩展双端队列的左侧。注意,左边的序列会导致反转迭代参数中元素的顺序。
在这里插入图片描述

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

返回deque中的x位置(在start之后开始并且在stop之前)。返回第一个匹配,如果未找到则引发ValueError

版本3.5中的新功能。

insert(i, x)

将x插入双端队列中的位置i中。

如果插入会导致有界双端超过maxlen,则会引发IndexError

版本3.5中的新功能。
在这里插入图片描述

pop()

从双端队列的右侧移除并返回一个元素。如果没有元素,则抛出一个IndexError

popleft()

从双端队列的左侧移除并返回一个元素。如果没有元素,则抛出一个IndexError

remove(value)

删除第一次出现的值。如果没有找到,抛出一个 ValueError。
在这里插入图片描述

reverse()

原位反转deque的元素然后返回None。

版本3.2中的新功能。

rotate(n=1)

向右旋转 n步。如果n为负数,则向左旋转。

当双端队列不为空时,向右旋转一步相当于d.appendleft(d.pop()),向左旋转一步相当于d.append(d.popleft())。
在这里插入图片描述

maxlen

Deque对象还提供一个只读属性,maxlen:
一个双端队列的最大大小或None(无边界)。

版本3.1中的新功能。

4.3 注意事项

除上述外,双端支持迭代,序列化(pickling),len(d)reversed(d)copy.copy(d)copy.deepcopy(d),成员测试in操作,下标引用,如d[-1]索引访问在两端都是O(1),但在中间减慢到O(n)。对于快速随机访问,请改用列表。

在3.5版本开始,支持__add__()__mul__()__imul__()

4.4 实际用途

本节介绍了使用deques处理问题的各种方法。

(1)有界长度deques提供类似于Unix中的tail过滤器的功能:

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

(2)使用deques的另一种方法是通过向右追加并弹出到左侧来维护一系列最近添加的元素:

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

(3)一个循环调度器可以用存储在deque中的输入迭代来实现。值从位置零处的活动迭代器产生。如果该迭代器已耗尽,则可以使用popleft()将其删除; 否则,它可以通过rotate()方法循环回到最后:

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

(4)rotate()方法可以模拟deque的切片和删除方法。例如,纯Python实现del d[n]依赖于rotate()定位要弹出的元素:

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

要实现deque切片,请用类似的方法使用rotate()将目标元素置于双端队列的左侧。popleft()删除旧条目,extend()添加新条目,然后反转旋转。对这种方法微调,能实现很多堆栈的其他操作,如dup,drop,swap,over,pick, rot,和roll

5. defaultdict 对象

5.1 class collections.defaultdict([default_factory[, …]])

返回一个新的类字典对象。 defaultdict是内置dict类的子类。它会覆盖一个方法并添加一个可写实例变量。其余功能与dict相同, 此处不赘述。

第一个参数default_factory 提供属性的初始值; 它默认为None。所有剩余的参数都被视为传递给dict构造函数,包括关键字参数。

5.2 方法

除标准dict操作外,defaultdict对象还支持以下方法:

__missing__(key)

如果default_factory属性为None,则以key为参数引发 KeyError异常。

如果default_factory不是None,则在没有参数的情况下调用它来为给定key提供默认值,将该值插入键的字典中并返回。

如果调用default_factory引发异常,则此异常将保持不变。

当找不到请求的key时,该方法由dict类的__getitem__()方法调用; 无论它返回或报错,都是通过__getitem__()

请注意,除了__getitem__()之外的任何操作都不会调用__missing__() 。这意味着get(),与普通字典一样,它将None作为默认值返回而不是使用 default_factory。

defaultdict 对象支持以下实例变量:

default_factory

该属性由__missing__()方法使用; 它从构造函数的第一个参数初始化(如果存在),如果不存在则为None 。

5.3 例子

使用 list 作为 default_factory,很容易将一系列键值对分组到列表字典中:

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

当第一次遇到每个key时,它还没有在映射中; 所以使用default_factory 函数自动创建并返回一个空的list。然后,list.append() 操作将值附加到新列表中。当再次遇到key时,查找会正常进行(返回该key的列表),然后 list.append()操作会向列表中添加另一个值。这种技术比使用等效技术的dict.setdefault()方法的更简单,更快捷:

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

设置default_factoryto为 int使 defaultdict可用于计数(如其他语言的包或multiset):

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

首次遇到字母时,映射中缺少该字母,因此 default_factory函数调用int()以提供默认计数为零。然后,增量操作会为每个字母构建计数。

int()始终返回零的函数只是常量函数的特例。创建常量函数的更快更灵活的方法是使用lambda函数,它可以提供任何常量值(不仅仅是零):

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

设置default_factoryto 为set使得 defaultdict构建集合字典非常有用:

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

6. namedtuple() 具有命名字段的元组的工厂函数

命名元组(Named tuples)为元组中的每个位置赋予含义,并允许更好的可读性,自我记录的代码。它们可以在使用常规元组的任何地方使用,并且它们添加了按名称而不是位置索引访问字段的功能。

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

返回名为typename的新元组的子类。新子类用于创建类似元组的对象,这些对象具有可通过属性查找访问的字段以及可索引和可​​迭代的字段。子类的实例也有一个有用的docstring(带有typename和field_names)和一个有用的__repr__() 方法,它以一种name=value格式列出元组内容。

field_names是字符串序列如['x', 'y']。或者,field_names可以是单个字符串,每个字段名由空格 和/或 逗号分隔,例如'x y''x, y'

除了以下划线开头的名称外,任何有效的Python标识符都可用于字段名。有效标识符由字母,数字和下划线,但不以数字或下划线开始,不能是keyword诸如class, for, return, global, pass, 或 raise

如果rename为true,则无效的字段名称将自动替换为位置名称。例如,['abc', 'def', 'ghi', 'abc']转换为['abc', '_1', 'ghi', '_3'],删除关键字def和重复的字段名abc

default可以是None或可以迭代的默认值。由于具有默认值的字段必须位于没有默认值的任何字段之后,因此默认值将应用于最右侧的参数。例如,如果字段名是['x', 'y', 'z']且默认值是(1, 2) ,那么x将是必需参数,y默认为1 ,默认为2。

如果定义了module,则将命名元组的__module__属性被设置为该值。

命名的元组实例没有每个实例的字典,因此它们是轻量级的,并且不需要比常规元组更多的内存。

版本3.1中已更改:添加了对rename的支持。

改变在3.6版本:在verbose和rename 参数成为关键字参数。

版本3.6中已更改:添加了module参数。

版本3.7中已更改:删除verbose 参数和_source属性。

版本3.7中已更改:添加了defaults参数和_field_defaults 属性。

6.2 例子

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

命名元组对于将字段名称分配给由csvsqlite3模块返回的结果元组特别有用:

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)

6.3 额外方法和属性

除了从元组继承的方法之外,命名元组还支持三个额外的方法和两个属性。为防止与字段名称冲突,方法和属性名称以下划线开头。

classmethod somenamedtuple._make(iterable)

从现有序列生成新实例或可迭代的类方法

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

somenamedtuple._asdict()

返回一个新的OrderedDict,映射字段名称到它们对应的值:

>>> p = Point(x=11, y=22)
>>> p._asdict()
OrderedDict([('x', 11), ('y', 22)])

版本3.1中更改:返回OrderedDict而不是常规的dict。

somenamedtuple._replace(**kwargs)

返回使用新值替换指定字段的命名元组的新实例:

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

列出字段名称的字符串元组。用于内省和从现有命名元组创建新的命名元组类型。

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

字典将字段名称映射到默认值。

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

要检索名称存储在字符串中的字段,请使用getattr() 函数:

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

要将字典转换为命名元组,请使用**(如Unpacking Argument Lists中所述):

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

由于命名元组是常规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

上面显示的子类设置__slots__为空元组。这有助于防止创建实例字典,从而降低内存需求。

子类化对于添加新的存储字段没有用。相反,只需从_fields属性创建一个新的命名元组类型:

>>> 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中更改:属性__doc__变得可写。

可以使用_replace()自定义原型实例来实现默认值:

>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
>>> janes_account = default_account._replace(owner='Jane')

也可以看看
由Jan Kaliszewski编写的Recipe for named tuple abstract base class with a metaclass mix-in。除了为命名元组提供抽象基类之外,它还支持基于元类的备用构造函数,该构造函数便于将子元素命名为元组的用例。
请参阅types.SimpleNamespace()基于底层字典而不是元组的可变命名空间。
有关为命名元组添加类型提示的方法,请参阅typing.NamedTuple()

7. OrderedDict 对象

有序词典就像常规词典一样,但它们记住了项目的插入顺序。迭代有序字典时,将按照首次添加键的顺序返回项目。

7.1 class collections.OrderedDict([items])

返回一个dict子类的实例,支持通常的dict 方法。一个OrderedDict是记住键第一次被插入的顺序的字典。如果新条目覆盖现有条目,则原始插入位置保持不变。删除条目并重新插入它会将其移至最后。

版本3.1中的新功能。

7.2 方法

popitem(last=True)

返回并删除一个(key, value)对。如果last=True,则以LIFO顺序(后进先出)返回对,如果为false 则以FIFO(先进先出)顺序返回 。
在这里插入图片描述

move_to_end(key, last=True)

将现有键移动到有序字典的另一端。该项目被移动到右端(如果last=True(默认)),或一开始( 如果last=False)。如果key不存在则引发KeyError

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

版本3.2中的新功能。

除了通常的映射方法之外,有序字典还支持使用reversed()进行反向迭代。

OrderedDict对象之间的等式测试是对顺序敏感的,并且实现为list(od1.items())==list(od2.items())。OrderedDict对象和其他 Mapping对象之间的等式测试对常规字典顺序不敏感。这允许在使用常规字典的任何地方替换OrderedDict对象。

3.5版本改变:items,keys,和 OrderedDict的view,支持使用reversed()进行反向迭代。

在版本3.6中更改:接受PEP 468,保留了传递给OrderedDict构造函数及其update() 方法的关键字参数的顺序。

7.3 用途

由于有序字典会记住其插入顺序,因此它可以与排序结合使用以生成排序字典:

>>>
>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

删除条目时,新排序的字典会保持其排序顺序。但是,当添加新key时,key将附加到末尾,并且不会保留排序。

创建一个有序的字典变体也很简单,它可以记住上次插入密钥的顺序。如果新条目覆盖现有条目,则原始插入位置将更改并移至结尾:

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

    def __setitem__(self, key, value):
        if key in self:
            del self[key]
        OrderedDict.__setitem__(self, key, value)

有序字典可以与Counter类组合,以便Counter记住第一次遇到的排序元素:

class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

8. UserDict 对象

该类充当字典对象的包装器。对这个类的需求已经被直接从dict子类的功能部分取代;但是,这个类可以更容易使用,因为底层字典可以作为属性访问。

class collections.UserDict([initialdata])

模拟字典的类。实例的内容保存在常规字典中,可通过UserDict实例的data属性访问。如果提供了initialdata,则使用其内容对data进行初始化; 请注意,不会保留对initialdata的引用,允许将其用于其他目的。

除了支持映射的方法和操作之外, UserDict实例还提供以下属性:

data
用于存储UserDict 类内容的真实字典。

9. UserList 对象

此类充当列表对象的包装。对于您自己的类似列表的类,它是一个有用的基类,它可以从它们继承并覆盖现有方法或添加新方法。通过这种方式,可以向列表添加新行为。

直接从list子类的能力部分取代了对这一类的需求; 但是,这个类可以更容易使用,因为底层列表可以作为属性访问。

class collections.UserList([list])
模拟列表的类。实例的内容保存在常规列表中,可通过 实例data属性访问UserList。实例的内容最初设置为列表副本,默认为空列表[]。 list可以是任何可迭代的,例如真正的Python列表或UserList对象。

除了支持可变序列的方法和操作之外, UserList实例还提供以下属性:

data
一个list对象用于存储UserList类内容。

子类要求:UserList子类应该提供一个构造函数,可以使用无参数或一个参数调用它。列出返回新序列的操作尝试创建实际实现类的实例。为此,它假定可以使用单个参数调用构造函数,该参数是用作数据源的序列对象。

如果派生类不希望符合此要求,则需要覆盖此类支持的所有特殊方法; 有关在这种情况下需要提供的方法的信息,请咨询消息来源。

10. UserString 对象

该类UserString充当字符串对象的包装器。直接从str子类的能力部分取代了对这一类的需求; 但是,这个类可以更容易使用,因为底层字符串可以作为属性访问。

class collections.UserString(seq)
模拟字符串对象的类。实例的内容保存在常规字符串对象中,可通过UserString实例data属性访问 。实例的内容最初设置为seq的副本。seq参数可以是可被转化成使用内置在一个字符串的任何对象 str()的功能。

除了支持字符串的方法和操作外, UserString实例还提供以下属性:

data
用于存储UserString类内容的str对象 。

3.5版本改变:新的方法__getnewargs____rmod__casefoldformat_mapisprintable,和maketrans

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值