与列表一起,元组是Python中使用的一些最常见的集合。它们主要有两个基本用例:
-
作为记录。
-
作为不可变列表。
下面,我们将讨论两者的功能,并着重讨论元在实际应用中的不变性。
元数作为记录
作为记录,元数保存数据,其每个项表示一个字段。在这种情况下,项目的顺序非常重要,因为元组中的每个位置都表示特定字段。
字段的表示取决于其位置。由于位置很重要,因此用作数据记录的元数的大小必须固定和知道,以及每个项目的重要性。
t_movie_1 = ('The Godfather', 1972, " Francis Ford Coppola")
# Tuple as a record of movie details of form (title, year of production, director)
title, year, director = t_movie_1
# tuple unpacking
t_movie_2 = ('Pulp fiction', 1994, " Quentin Tarantino")
list_movies = (t_movie_1, t_movie_2)
#a list of tuple movie records
for movie_title, movie_year, movie_director in list_movies:
print(movie_title)
#The Godfather
#Pulp fiction
在前面的代码块中,注释行三:元组解压缩会影响元组的项目,以便仅在一个表达式中将它们分隔在不同的变量上。
此机制(解压缩)可用于每个可迭代对象,而不仅仅是元数。需要尊重的要点是,元组中的项数必须与表达式左侧的变量数相同,除非您使用 *
来捕获列表中的其他项。
下面的代码块显示了 *
使用 解包元组。
t_movie_1 = ('The Godfather', 1972, "Francis Ford Coppola")
# Tuple as a record of movie details of form (title, year, director)
title, year, director = t_movie_1
print ("%s, %s, %s" %(title, year, director))
#The Godfather, 1972, Francis Ford Coppola
title, *rest = t_movie_1
print (title, rest)
#The Godfather , [1972, 'Francis Ford Coppola']
在某些情况下,仅根据记录(元组)的位置来解释字段符号不是非常可读和实用的。这就是为什么 python 标准库中的包集合提供命名图普尔对象的原因。集合.nametuple是一个工厂函数,它创建具有字段名称和类名称的元组子类。
from collections import namedtuple
Movie = namedtuple("Movie", ["title", "year", "director"])
#creation of the namedtuple subclass with Movie as the name of the class and a list of
#fileds
m1 = Movie('The Godfather', 1972, " Francis Ford Coppola")
#instanciation of the named tuple m1
print (m1)
#Movie(title='The Godfather', year=1972, director=' Francis Ford Coppola')
print(m1.title)
#The Godfather
许多人更喜欢将字典用作数据记录容器,而不是元数。虽然字典在此范围内是全手的,但它们消耗了太多的内存,与元组相比,甚至与命名图普尔这没有错,但它也不是100%准确。
在探索这些容器时,您可能会惊讶地发现,不可变的元数在某些情况下是可变的。
让我们直接从一个例子开始:
t_movie_1 = ('The Godfather', 1972, "Francis Ford Coppola")
# Tuple as a record of movie details of form (title, year, director)
t_movie_2 = (['The Godfather', 'le parrain'], 1972, "Francis Ford Coppola")
#same movie tuple but with a list that contains the name of the movie in different lanquages
t_movie_1[0] = "le parrain"
#TypeError: 'tuple' object does not support item assignment
t_movie_2[0].append("el padrino")
print(t_movie_2)
#(['The Godfather', 'le parrain', 'el padrino'], 1972, 'Francis Ford Coppola')
第 t_movie_1
一个元组是一个元组,只有不可变对象作为项(int、str、..),因为根据定义,元组是不可变的。尝试影响第一个项的另一个值会引发 TypeError exception
。
但是,请注意第二个元组行为。将新值追加到第一个项效果良好,新值将添加到元组中的 t_movie_2
列表中。
的第 t_movie_2
一项是可变列表。为什么没有引发 TypeError 异常?
那么,元组包含对列表的引用,而不是列表本身的项目。列表方法 append
不会更改该引用,但它会向列表中添加新项。因此,元组项(对列表的引用)并没有真正更改。
t_movie_2 = (['The Godfather', 'le parrain'], 1972, "Francis Ford Coppola")
#same movie tuple but with a list that contains the name of the movie in different lanquages
print( id(t_movie_2[0]))
#2704848
t_movie_2[0].append("el padrino")
print(t_movie_2)
#(['The Godfather', 'le parrain', 'el padrino'], 1972, 'Francis Ford Coppola')
print( id(t_movie_2[0]))
#2704848
在前面的代码块中,元组内列表对象的 ID(排序中的引用)在 append
方法调用后没有更改。
从技术上讲,元组没有改变(它真的是不可变的)。这是其可变列表的内容确实发生了变化。
如果尝试影响新列表以代替元组内的旧列表, TypeError
则将引发 ,因为影响包括将现有列表引用替换为新列表,以具有与可变对象相同的行为。
t_movie_2 = (['The Godfather', 'le parrain'], 1972, "Francis Ford Coppola")
t_movie_2[0] = ['The Godfather', 'le parrain', 'el padrino']
#TypeError: 'tuple' object does not support item assignment
下图描述了将新项目追加到元组列表中时发生的情况。
当您 +=
在元组内的可变容器上使用运算符时,会出现一种非常典型的情况。让我们通过下面的示例来探讨它:
t_movie_2 = (['The Godfather', 'le parrain'], 1972, "Francis Ford Coppola")
t_movie_2[0] += 'el padrino'
#TypeError: 'tuple' object does not support item assignment
+=
操作员提出 TypeError
。你可以说这是正常的,我同意你 – 毕竟 +=
是一个分配运算符类似于 =
运算符,所以为什么期待不同的行为不同于这一个!
真正令人振奋的是,即使例外提出,影响也确实发生了;请参阅下面的代码+=
不是基本操作;如果检查此操作的汇编器代码,您就会意识到,首先附加列表,稍后引发异常,因此,即使有例外,追加也已完成。
结论
总之,元数是非常容易使用和非常有用的。它们主要用作数据记录或不可变列表。元数的不变性取决于其中的项目类型。概括,元组是不可变的。但是,其中可变项不是。