Records Revisited: 命名元组

未匹配的标注

事实上,数据类型的选择甚至比前一节已经暗示的还要丰富——今天的Python程序可以选择各种各样的内置核心类型还有建立在它们之上的扩展类型。比如,在前一章中的侧边栏为什么你将关心:字典 vs 列表中,我们看到如何使用列表和字典类表示类似记录的信息,也指出了字典提供了更容易记忆的,对数据打标签的键的优势。只要不需要可变性,元组就可以起到与列表类似的作用,使用位置来记录字段:

>>> bob = ('Bob', 40.5, ['dev', 'mgr']) # Tuple record
>>> bob
('Bob', 40.5, ['dev', 'mgr'])
>>> bob[0], bob[2] # Access by position
('Bob', ['dev', 'mgr'])

然而,和列表一样,元组中的字段数字承载的信息通常比字典的键名要少。下面是同样的记录被带有命名字段的字典记录的例子:

>>> bob = dict(name='Bob', age=40.5, jobs=['dev', 'mgr']) # Dictionary record
>>> bob
{'jobs': ['dev', 'mgr'], 'name': 'Bob', 'age': 40.5}
>>> bob['name'], bob['jobs'] # Access by key
('Bob', ['dev', 'mgr'])

事实上,如果需要可以将字典的一部分转换为元组:

>>> tuple(bob.values()) # Values to tuple
(['dev', 'mgr'], 'Bob', 40.5)
>>> list(bob.items()) # Items to tuple list
[('jobs', ['dev', 'mgr']), ('name', 'Bob'), ('age', 40.5)]

但额外做点工作,就可以实现同时提供记录字段的位置访问和命名访问的对象。比如,在第8章中提到的标准库 collections 模块中可用的 namedtuple 实用程序实现了一个扩展类型,它给元组添加了一些逻辑:允许组件可以同时按位置和属性名称访问,且如果需要能被转换为类似字典的形式来按访问。属性名称来自类且不完全是字典的键,但它们类似字典,易于记忆:

>>> from collections import namedtuple # Import extension type
>>> Rec = namedtuple('Rec', ['name', 'age', 'jobs']) # Make a generated class
>>> bob = Rec('Bob', age=40.5, jobs=['dev', 'mgr']) # A named-tuple record
>>> bob
Rec(name='Bob', age=40.5, jobs=['dev', 'mgr'])
>>> bob[0], bob[2] # Access by position
('Bob', ['dev', 'mgr'])
>>> bob.name, bob.jobs # Access by attribute
('Bob', ['dev', 'mgr'])

当需要时,转换为支持基于键的行为的字典:

>>> O = bob._asdict() # Dictionary-like form
>>> O['name'], O['jobs'] # Access by key too
('Bob', ['dev', 'mgr'])
>>> O
OrderedDict([('name', 'Bob'), ('age', 40.5), ('jobs', ['dev',
'mgr'])]) # 3.3
>>> O
{'name': 'Bob', 'age': 40.5, 'jobs': ['dev', 'mgr']} # 3.10

可以看到:命名元组是元组/类/字典的混合。它们也代表了一种经典的妥协。作为额外功能的交换,它们需要额外的代码(在前面例子中的导入类型,创建类的两个起始行),并导致一些性能消耗来完成这个神奇的过程。(简言之,命名元组构建了新类,拓展了元组类型,为每个命名的字段插入一个property访问器方法,该方法将名称映射到其位置——一种依赖于将在第8部分中探索的高级主题的技术,且使用格式化代码字符串而非像装饰器和元类这样的类注释工具)。尽管如此,它们仍是当需要额外功能时,可以在内置类型如元组上构建自定义数据类型的很好的例子。

命名元组在Python 3.X,2.7,2.6(其中 _asdict 返回一个真的字典)中可用,且可能更早,虽然根据Python标准,它们依赖于相对现代的特性。它们还是扩展,而非核心类型——它们位于标准库中和第5章中的 FractionDecimal 属于同一类别——所以为了获取更多细节,请参阅Python库手册(委托给了Python手册)。

然而,作为一个快速预览,元组和命名元组都支持将在第13章中学习的元组赋值解压缩,还有将在第14章第20章中探索的迭代上下文(注意这里的位置初始化值:命名元组可以按名称、位置或两者一起来接收):

>> bob = Rec('Bob', 40.5, ['dev', 'mgr']) # For both tuples and named tuples
>>> name, age, jobs = bob # Tuple assignment (Chapter 11)
>>> name, jobs
('Bob', ['dev', 'mgr'])
>>> for x in bob: print(x) # Iteration context (Chapters 14, 20)
...prints Bob, 40.5, ['dev', 'mgr']...

元组的解包赋值不太适用于字典,除非获取和转换键和值,并在它们上面假设或强加一个位置排序(字典不是序列),且通过键(而非值)迭代(注意这里的字典字面量形式:dict的一种可选形式):

>>> bob = {'name': 'Bob', 'age': 40.5, 'jobs': ['dev', 'mgr']}
>>> job, name, age = bob.values()
>>> name, job # Dict equivalent (but order may vary)
('Bob', ['dev', 'mgr'])
>>> for x in bob: print(bob[x]) # Step though keys, index values
...prints values...
>>> for x in bob.values(): print(x) # Step through values view
...prints values...

当在第27章中学习用户自定义类如何比较时,请关注这个记录表示主题的最后一次的重复讨论;将发信啊:类也使用名称来给字典添加标签,但也能提供程序逻辑来在同一个包中处理记录的数据。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 查看所有版本


暂无话题~