Python:可变长度元组

9

[Python 3.1]

我正在跟进元组应该具有已知长度的设计概念(请参见此评论),并且在大多数情况下,未知长度的元组应该被替换为列表。我的问题是,在什么情况下应该偏离这个规则?

例如,我了解到元组比列表更快地从字符串和数字字面量创建(请参见另一个评论)。因此,如果我有性能关键代码,其中有许多像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个回答

19

在我看来,元组和列表之间唯一有趣的区别是:列表是可变的,而元组则不是。人们提到的其他区别在我看来似乎完全是人为的:元组就像结构体,而列表就像数组(这就是“元组应该是已知长度”的原因)。但结构与不可变性有什么关系呢?并没有。

唯一重要的区别是语言所做的区分:可变性。如果需要修改对象,请使用列表。如果需要将对象哈希化(作为字典中的键或集合中的元素),则需要它是不可变的,因此请使用元组。就是这样。


3

我总是使用最合适的数据结构,并不会太在意一个元组是否会节省半毫秒的时间。预先混淆代码通常在最后并没有用。如果代码运行得太慢,你可以稍后进行分析,仅更改真正重要的0.01%的代码。

你所谈论的所有内容都与Python版本的实现以及它运行的硬件有关。你可以自己计时来看看它们在你的机器上的表现。

这方面的一个常见例子就是Python中“旧的不可变字符串连接速度较慢”的说法。这个说法大约在10年前是正确的,但后来在2.4或2.5中改变了实现方式。如果你进行自己的测试,它们现在比列表运行得更快,但人们今天依然相信这个说法,并使用一些愚蠢的构造,实际上跑得更慢!


也许在你说别人没有之前,你应该进行一些分析。尝试使用timeit从数字和字符串字面值构建元组和列表,看看会发生什么。另外,关于混淆的Python是什么东西?元组如何混淆任何内容?对我来说似乎非常清晰。 - aaronasterling
如果您在代码中使用元组,而列表会更清晰,则这是混淆代码。一个元组本身可能对任何人都很清楚,但它是一个更大的代码块的一部分。请阅读Ned Batchelder的回答。如果需要其中一个,请使用它。如果需要另一个,请使用它。元组和列表都可以在常数时间O(1)内构建。那么为了一点速度而妥协设计是否值得呢? - nate c

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视为堆栈或队列并弹出值的更罕见的情况下,您可能需要将其转换为列表。 但我们不能预先解决所有可能的问题。
有时需要思考。

当你在进行设计时,你会有一个目的来设计元组。那就是为了对数据进行一种有意义的结构化处理。如果元素数量是固定的,那就使用元组;如果元素数量是可变的(即可修改的),那就使用列表。


1
@max:“性能损失太小,不用担心吗?”是的。这关乎到意义。在这种情况下,性能并不重要。如果你需要更快的速度,找到正确的算法。如果你可以证明你有正确的算法,并且可以证明Python实现过慢,那么就针对那个问题切换到C语言。 - S.Lott
3
如果您有一个大小固定的对象,但需要修改其元素,该怎么办?或者,如果您有一个变量大小的对象,但需要将其用作键,该怎么办? - Ned Batchelder
2
听起来对我来说有点过于简单化了...;-) - martineau
1
@max:一个通用的“sumproduct”并不是很合适。在大多数应用中,您实际上知道您的向量的大小,并且您实际上应该编写适合实际向量实际大小的sumproduct。您真正需要的不是一个通用的、解决所有可能问题的通用sumproduct函数。您真正需要的是一个专门用于对(x、y、z、r)向量进行坐标变换的sumproduct4 - S.Lott
1
@S.Lott:我不太同意。我使用sumproduct函数来计算股票组合的当前价值,例如,对于组合中的所有股票i,其价值为(quantity(i) * price(i))之和。组合大小是可变的。 - max
显示剩余6条评论

1
在这种情况下,您应该考虑使用numpy和numpy数组。
转换为和从numpy数组中转换存在一些开销,但如果您正在进行大量计算,它将会更快。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接