元组的基本概念
元组的基本操作
在 Python 中,使用圆括号括起逗号分隔的不同的数据项
会被视为一个元组(tuple)。在不引起歧义的情况下,最外层的括号可以省略,例如:
因此,要注意有些时候,一个元素后面不小心多加了一个逗号,它会被当做只有一个元素的元组处理,可能会导致类型错误等问题。
也可以使用 tuple
类从一个可迭代对象中构造元组。
给定一个元组,可以使用赋值语句将其中包含的元素分配给一系列变量,例如:
这种操作称为元组的解包(unpack)。在解包时,变量的个数要和元组的元素个数一致,否则会导致错误。
如果解包时,只需要前面的几个元素,那么可以在最后一个变量前加上星号 *
,将解包后剩下的值塞进该变量中:
元组是一种不可变类型,任何尝试对元组元素的修改都会发生错误。元组仅支持 .count()
和 .index()
方法,分别用于统计某个元素出现的次数以及查找某个元素的位置。
实际上以上元组的特性,列表也能做到。看起来元组就是一个不能修改的列表,那为何 Python 要引入这种类型呢?
元组最大的特性也就是其不可变,而有些时候(例如在函数式编程中)并不需要改变元素的值,此时为了防止元素意外变动,就需要使用元组。例如在某些大型工程中函数众多,可能会有某个排序函数意外改变原有序列,如果使用元组就能提前发现问题所在。
元组这种不可变的特点使得它可以作为字典的键:
而列表则不行。
在使用使用中,常用元组来表达某一个结构形式的数据,例如以下是一个列表元组,列表中的每一个元组在同一索引值处,表达的都是同一含义的数据:
使用命名元组
以上使用元组来表达结构形式的数据,但这种结构只能通过索引来访问其元素。如果元组中的元素较多,不仅操作起来很麻烦,而且比较容易出错。
命名元组是一种特殊的元组,命名元组的元素既可以使用名称访问,也可以使用索引值访问,大大增加了元组的可读性。
命名元组并不是内置的数据类型,而是标准库的一部分,因此首先需要导入命名元组:
使用 namedtuple
的构造函数可以定义一个子类命名元组,其构造函数的完整形式为:
其中各参数的含义为:
typename
是返回的命名元组子类的类名,创建命名元组相当于创建了一个新类field_names
是命名元组各元素的名称,是一个由字符串组成的列表,其中的字符串必须为合法的标识符;或者field_names
也可以是一个字符串,各元素名称使用逗号或空格隔开- 当
rename
设置为True
时,如果field_names
中包含保留关键字或重复的变量名,则会自动重命名为_1
、_2
等 - 如果给定
defaults
值,则它应该是一个由默认值组成的可迭代对象。由于具有默认值的参数必须位于没有默认值的任何参数之后,如果默认值少于命名元组的元素个数,默认值将应用于最右边的元素。如果默认值多于命名元组的元素个数,将引起错误 - 如果设置了
module
参数,那么该类将位于该模块下,因此该自定义类的__module__
属性将被设置为该参数值
以下是一个简单的示例,构造了一个类型的命名元组,并由该类型的命名元组生成具体的元组对象:
命名元组对象可以通过访问元素的字段名称来访问其属性,这更加准确、方便:
命名元组创建的类继承于元组,并且包含以下额外属性和方法:
._fields
类属性返回其所有字段名称,以下给出了一个示例,并注意 rename
参数的作用效果:
如果没有设置 rename
参数,则会直接发生错误。
._field_defaults
属性返回所有有默认值的字段及其默认值组成的字典。如果没有默认值,那么返回空字典。
._make(iterable)
用于从指定可迭代对象构建命名元组对象。这里需要注意的是,得到的命名元组参数和字段名是对应的,可以使用关键字参数的形式生成命名元组:
因此,调用该方法相当于在可迭代对象前加上星号将其变为一系列参数。
_replace(**kwargs)
方法可以用于从另一个命名元组得到其部分值被替换后的副本:
最后,._asdict()
方法用于把命名元组对象转换为 OrderedDict ,即一种字典对象。
改进的命名元组
Python 中可以使用另一种方式得到命名元组,这种方式得到的命名元组更简洁,并且可以保存类型信息。
如果对以下涉及的 Python 语法有疑问,也可以暂时忽略。
这种类型的命名元组通过继承创建,并且使用类属性表示各个字段,代码为:
类型注解是必须的,否则无法通过语法检查。Python 中的类型注解都不会实际检查类型是否匹配,不过它会为编写和阅读代码提供一定方便。
它的使用方法也和前一种命名元组一致:
这种命名元组也具有以上所使用的一些特别的属性和方法。在实际使用时可以任意选取一种命名元组。个人比较推荐这一种,因为它的定义比较简洁易懂。