collections模块中的namedtuple和defaultdict

collections模块是Python中对内置类型(dict, list, tuple)的拓展。就是说它们本身具有普通内置类型的所有特性,并添加了新的功能。

namedtuple()

namedtuple(typename, field_names)tuple的子类,定义的元组可以通过属性访问,也易于理解,self-document。field_names可以是列表或者是通过空格或者逗号分隔的字符串。

>>> from collections import namedtuple
>>> Point = namedtuple('point', 'x, y')
>>> p = Point(3, y=4)
>>> p
point(x=3, y=4)
>>> p[0]
3
>>> p.x
3
>>> x, y = p
>>> print(x, y)
3 4

namedtuple有几个比较有用的方法和属性

  • SomeNamedTuple._make(iterable)类方法,从可迭代对象中取值,长度必须和传入的field_names一致,返回一个新的对象
  • somenamedtuple._asdict()方法返回一个有序字典(OrderedDict)
  • somenamedtuple._replace(**kwargs)方法替换值,返回一个新的对象,原来的不变
  • somenamedtuple._fields属性返回键名(field name),可用于创建新的named tuple

再来看两个官方文档上的例子读取csv文件或者从sqlite读取

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)

defaultdict

defaultdict([default_factory[,...]])顾名思义继承自dict它接收一个函数(default_factory)作为参数但它除了dict所有的属性和方法外,另外加了__missing__方法和default_factory属性。这两个的作用就是访问不存在的key时它会新增一个key,value为调用default_factory的值。

>>> from collections import defaultdict
>>> d = defaultdict(list) # 默认函数为list
>>> print(d.items())  # 创建空字典
dict_items([])
>>> d.default_factory
<class 'list'>
>>> d['key']  # 调用会设置该key的值为空list并返回
[]
>>> print(d.items())
dict_items([('key', [])])

我们可以利用这个特性来做很多事,比如说给一个包含元组的的列表分类整理。

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

上面的代码其实可以用dictsetdefault实现,但效率和可读性差点,如下

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

最后看一下自定义default_factory参数

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

一个是比较讨巧的例子,利用了递归。

>>> tree = lambda: defaultdict(tree)
>>> d = tree()
>>> d['colours']['favourite'] = 'yellow'
>>> d['animals']['pets']['name'] = ['cat', 'dog']
>>> import json
>>> print(json.dumps(d))
{"colours": {"favourite": "yellow"}, "animals": {"pets": {"name": ["cat", "dog"]}}}

References

  1. https://docs.python.org/
  2. https://github.com/