Python元组:不可变的神话
不变性是否延伸到它们的组成部分?
连同列表, 元组是Python中使用的一些最常见的集合。它们主要有两个基本用例:
作为记录。
一成不变的列表。
下面,我们将讨论两者的功能,并强调元组在实际应用中的不可变性。
你也可以喜欢: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标准库中的包集合提供了名称对象。这个Collections.namedtuple是一个工厂函数,它创建带有字段名和类名的元组子类。
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
很多人更喜欢字典用作数据记录容器而不是元组。虽然字典在此范围内是手动的,但与元组相比,甚至与元组相比,它们消耗了太多的内存。名称.
元组为“不变列表”
关于Pyhton的许多入门教程提到,元组是不可变的列表。这并不是错误的,但它也不是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
下图描述了在元组中将新项追加到列表时发生的情况。
tuple immutability当您使用运算符时,会出现一种非常典型的情况。+=在元组中的可变容器上。让我们用下面的例子来探讨这个问题:
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。你可以说这是正常的,我同意你–毕竟+=是类似于=操作员,那么为什么期望一个不同的行为与此不同呢!
真正让人心烦意乱的是,即使有例外,做作也会发生;参见下面的代码。
t_movie_2 = ([‘The Godfather’, ‘le parrain’], 1972, “Francis Ford Coppola”)
try:
t_movie_2[0] += [‘el padrino’]
except TypeError as ex:
print (str(ex))
#‘tuple’ object does not support item assignment
print (t_movie_2)
#([‘The Godfather’, ‘le parrain’, ‘el padrino’], 1972, ‘Francis Ford Coppola’)
这很奇怪,虽然可以解释。这个+=不是基本操作;如果您检查此操作的汇编程序代码,您将意识到,首先添加了列表,然后在异常中引发,因此即使有异常,追加也已经完成。
结语
总之,元组是非常容易使用和非常有用的。它们主要用作数据记录或不可更改的列表。元组的不可变性取决于元组中项目的类型。要泛化,元组是不可变的。但是,其中的可变项不是。