Python:可变长度元组

9 投票
4 回答
11287 浏览
提问于 2025-04-16 07:15

[Python 3.1]

我在关注一个设计理念,就是元组(tuple)应该是长度已知的(可以参考这个评论),而长度未知的元组在大多数情况下应该用列表(list)来替代。我的问题是,在什么情况下我可以不遵循这个规则呢?

举个例子,我知道从字符串和数字字面量创建元组比创建列表要快(可以参考另一个评论)。所以,如果我有一些对性能要求很高的代码,比如有很多计算像是sumproduct(tuple1, tuple2),我应该把它们重新定义为使用列表,尽管这样会影响性能吗?(sumproduct((x, y, z), (a, b, c))的定义是x * a + y * b + z * c,而它的参数长度是未知但相等的)。

还有,当我使用def f(*x)时,Python会自动生成一个元组。我想这不是我每次使用都需要转换成列表的东西吧。

顺便问一下,(x, y, z)[x, y, z]创建得更快吗?(这里是指变量而不是字面量)

4 个回答

2

在什么情况下我应该偏离“元组应该是已知长度”的规则呢?

没有。

这其实是个关于意义的问题。如果一个对象的意义是基于固定数量的元素,那么它就是一个元组。比如 (x,y) 坐标、(c,m,y,k) 颜色、(lat, lon) 位置等等。

元组的元素数量是根据问题的领域和具体情况来决定的。

设计一个元素数量不确定的元组是没有什么意义的。我们什么时候会从 (x,y) 变成 (x,y,z),再到 (x,y,z,w) 的坐标呢?这可不是简单地把一个值加到列表里那么简单。如果我们是从二维坐标转到三维坐标,通常需要一些复杂的数学来映射这些坐标系统,而不是简单地往列表里添加一个元素。

从 (r,g,b) 颜色变成其他颜色意味着什么呢?在 rgb 系统中,第四种颜色是什么?那么在 cmyk 系统中,第五种颜色又是什么呢?

元组的大小是不会改变的。

*args 是一个元组,因为它是不可变的。没错,它可以有不确定数量的参数,但这只是一个少见的例外,和已知、固定大小的元组相比。


那么对于一个长度不确定的元组该怎么办呢?这个例外情况非常重要,以至于我们有两个选择。

  1. 拒绝元组是固定长度的这个想法,认为它受到问题的限制。因为这个例外情况,(x,y) 坐标和 (r,g,b) 颜色的概念完全没有价值,都是错误的。固定长度的元组?绝对不行。

  2. 总是把所有的 *args 转换成列表,以便始终遵循设计原则。转换成列表?永远如此。

我喜欢这种非黑即白的选择,因为它让软件工程变得简单而不需要思考。

也许在这些特殊情况下,确实需要一点“思考”的成分。只是一点点。

没错,*args 是一个元组。是的,它的长度不确定。没错,它是一个例外情况,在这里“由问题领域决定的固定性”被“简单的不可变性”所超越。

这让我们在某些情况下有了第三个选择:如果一个序列因为其他原因是不可变的,你永远不会去改变它,那么它可以是一个不定大小的元组。在更少见的情况下,如果你因为把 *args 当作栈或队列来使用而需要弹出值,那么你可能想把它变成一个列表。但我们无法提前解决所有可能的问题。

有时候,确实需要思考。


在设计时,你设计一个元组是有原因的。是为了给你的数据施加一个有意义的结构。元素数量固定?用元组。元素数量可变(也就是可变的)?用列表。

3

我总是选择最合适的数据结构来完成任务,并不太在意使用元组能否让我节省半毫秒的时间。提前让代码变得复杂通常不会带来好处。如果代码运行得太慢,你可以稍后进行性能分析,找出那0.01%真正影响性能的代码进行修改。

你提到的所有问题都和你使用的Python版本以及运行它的硬件有关。你可以自己测试一下,看看在你的机器上这些操作的时间是多少。

一个常见的例子就是“旧版不可变字符串拼接很慢”这个说法。在大约十年前,这个说法是对的,但在2.4或2.5版本中,他们改变了实现方式。如果你自己测试一下,现在字符串拼接的速度比列表还快,但很多人仍然相信这个说法,使用一些实际上运行得更慢的奇怪写法!

19

在我看来,元组和列表之间唯一有趣的区别就是:列表是可以改变的,而元组则不能。其他人提到的区别对我来说完全是人为的:元组像结构体,列表像数组(这就是“元组应该是固定长度”的来源)。但是,结构体的特性和不可变性有什么关系呢?其实没有关系。

唯一重要的区别就是编程语言所定义的:可变性。如果你需要修改这个对象,肯定要用列表。如果你需要把这个对象作为字典的键,或者作为集合的元素,那么你需要它是不可变的,所以就用元组。就这么简单。

撰写回答