56

我现在正在为一门课学习 Python,我们只是将元组作为数据类型之一。我阅读了关于它的 Wikipedia 页面,但是,我无法弄清楚这种数据类型在实践中在哪里有用。我可以举一些例子,也许在 Python 中,需要一组不可变的数字吗?这与列表有何不同?

4

11 回答 11

65
  • 每当您想从函数返回多个结果时,都会使用元组。
  • 由于它们是不可变的,它们可以用作字典的键(列表不能)。
于 2008-09-03T16:18:53.493 回答
20

当您需要将多个数据组合到您的键中并且不想为它创建一个类时,元组是很好的字典键。

a = {}
a[(1,2,"bob")] = "hello!"
a[("Hello","en-US")] = "Hi There!"

我主要使用这个特性来创建一个字典,其中的键是网格顶点的坐标。但是,在我的特定情况下,所涉及的浮点数的精确比较工作正常,这对于您的目的可能并不总是正确[在这种情况下,我可能会将您传入的浮点数转换为某种定点整数]

于 2008-09-03T16:20:59.447 回答
10

我喜欢这个解释

基本上,当有一个常量结构时,你应该使用元组(第一个位置总是保存一种类型的值,第二个位置总是保存另一种类型的值,依此类推),并且列表应该用于同类值的列表。

当然总有例外,但这是一个很好的一般准则。

于 2008-09-07T13:12:28.040 回答
10

考虑它的最佳方法是:

  • 元组是其字段没有名称的记录。
  • 当您不想指定字段名称时,您可以使用元组而不是记录。

所以不要写这样的东西:

person = {"name": "Sam", "age": 42}
name, age = person["name"], person["age"]

或者更冗长:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("Sam", 42)
name, age = person.name, person.age

你可以写:

person = ("Sam", 42)
name, age = person

当您想要传递只有几个字段的记录或仅在少数地方使用的记录时,这很有用。在这种情况下,使用字段名称指定一个全新的记录类型(在 Python 中,您将使用对象或字典,如上所述)可能过于冗长。

元组起源于函数式编程领域(Haskell、OCaml、Elm、F# 等),它们通常用于此目的。与 Python 不同,大多数函数式编程语言都是静态类型的(变量只能保存一种类型的值,并且该类型在编译时确定)。静态类型使元组的作用更加明显。例如,在 Elm 语言中:

type alias Person = (String, Int)

person : Person
person = ("Sam", 42)

这突出了这样一个事实,即特定类型的元组总是应该有固定数量的固定顺序的字段,并且每个字段都应该是相同的类型。在这个例子中,一个人总是两个字段的元组,一个是字符串,另一个是整数。

以上与 list 形成鲜明对比, list应该是可变长度(每个列表中的项目数通常不同,并且您编写添加和删除项目的函数)并且列表中的每个项目通常是相同的类型. 例如,您将拥有一个人员列表和另一个地址列表 - 您不会在同一个列表中混合人员和地址。而在同一个元组中混合不同类型的数据是元组的重点。元组中的字段通常具有不同的类型(但并非总是如此——例如,您可以有一个(Float, Float, Float)元组来表示 x、y、z 坐标)。

元组和列表通常是嵌套的。有一个元组列表是很常见的。您可以拥有一个 Person 元组列表以及一个 Person 对象列表。您还可以拥有一个值为列表的元组字段。例如,如果您有一个地址簿,其中一个人可以有多个地址,您可以有一个类型为 的元组(Person, [String])。该[String]类型通常在函数式编程语言中用于表示字符串列表。在 Python 中,您不会写下类型,但您可以以完全相同的方式使用类似的元组,将Person对象放在元组的第一个字段中,将字符串列表放在第二个字段中。

在 Python 中,会出现混淆,因为该语言没有强制执行编译器在静态类型函数语言中强制执行的任何这些做法。在这些语言中,您不能混合不同类型的元组。例如,你不能(String, String)从一个类型说它返回一个(String, Integer)元组的函数返回一个元组。当类型表示您计划返回元组时,您也不能返回列表,反之亦然。列表严格用于不断增长的项目集合,元组严格用于固定大小的记录。如果您愿意,Python 不会阻止您违反任何这些规则。

在 Python 中,有时会将列表转换为元组以用作字典键,因为 Python 字典键需要是不可变(即常量)值,而 Python 列表是可变的(您可以随时添加和删除项目)。这是针对 Python 中特定限制的解决方法,而不是作为计算机科学概念的元组的属性。

所以在 Python 中,列表是可变的,而元组是不可变的。但这只是一种设计选择,而不是计算机科学中列表和元组的内在属性。您也可以拥有不可变列表和可变元组。

在 Python 中(使用默认的 CPython 实现),在大多数情况下,元组也比对象或字典快,因此偶尔会出于这个原因使用元组,即使使用对象或字典命名字段会更清晰。

最后,为了更清楚地表明元组是另一种记录(而不是另一种列表),Python 还命名了元组

from collections import namedtuple

Person = namedtuple("Person", "name age")

person = Person("Sam", 42)
name, age = person.name, person.age

这通常是最好的选择 - 比定义一个新类要短,但字段的含义比使用字段没有名称的普通元组时更明显。

不可变列表对许多用途都非常有用,但是这个话题太复杂了,无法在这里回答。主要的一点是,不能改变的事情比可以改变的事情更容易推理。大多数软件错误来自以意想不到的方式变化的事物,因此限制它们可以改变的方式是消除错误的好方法。如果您有兴趣,我建议您阅读有关 Elm、Haskell 或 Clojure 等函数式编程语言的教程(Elm 是最友好的)。这些语言的设计者认为不变性非常有用,以至于所有列表在那里都是不可变的。(您无需更改列表以添加或删除项目,而是创建一个包含添加或删除项目的新列表. 不变性保证列表的旧副本永远不会改变,因此编译器和运行时可以通过在新列表中重用旧列表的部分并在剩余部分较长时对剩余部分进行垃圾收集来使代码运行良好需要。)

于 2018-09-25T14:45:09.760 回答
4

元组和列表通常具有相同的用途。不可变数据类型通常有很多好处,主要是关于并发问题。

因此,当您的列表本质上不是易变的并且您需要保证没有消费者更改它时,您可以使用元组。

典型示例是应用程序中的固定数据,如公司部门、类别等。如果此数据发生更改,通常单个生产者会重建元组。

于 2008-09-03T16:25:17.690 回答
2

当您总是将两个或多个对象作为一组处理时,我发现它们很有用。

于 2008-09-03T16:19:14.007 回答
2

元组是一系列值。这些值可以是任何类型,并且它们由整数索引,因此元组不像列表。最重要的区别是元组是不可变的。

元组是逗号分隔的值列表:

t = 'p', 'q', 'r', 's', 't'

将元组括在括号中是一种很好的做法:

t = ('p', 'q', 'r', 's', 't') 
于 2016-09-22T11:54:05.827 回答
1

就功能而言,列表始终可以替换元组(显然,作为字典中的键除外)。但是,元组可以使事情变得更快。例如,Java 中的不可变字符串也是如此——您何时需要无法更改字符串?绝不!

我刚刚阅读了一篇关于限制你可以做什么以制作更好的程序的体面的讨论;为什么函数式编程很重要

于 2008-09-03T16:20:00.853 回答
1

元组对于存储多个值很有用。正如您所注意到的,元组就像一个不可变的列表 - 例如,一旦创建,您就无法添加/删除/交换元素。

不可变的一个好处是,由于元组是固定大小的,它允许运行时执行某些优化。当在返回值或函数参数的上下文中使用元组时,这尤其有用。

于 2008-09-03T16:23:48.037 回答
0

除了在语法上需要它们的地方,比如字符串 % 操作和多个返回值,我使用元组作为轻量级类的一种形式。例如,假设您有一个对象,它从一个方法将不透明的 cookie 传递给调用者,然后将其传递给另一个方法。元组是将多个值打包到该 cookie 中的好方法,而无需定义一个单独的类来包含它们。

不过,我试图对这种特殊用途保持谨慎。如果在整个代码中大量使用 cookie,最好创建一个类,因为它有助于记录它们的使用。如果它们只用在一个地方(例如一对方法),那么我可能会使用一个元组。无论如何,因为它是 Python,您可以从一个元组开始,然后将其更改为自定义类的实例,而无需更改调用者中的任何代码。

于 2008-09-09T03:56:09.683 回答
0

元组用于:

  1. 您希望元素序列不可变的地方
  2. 在元组赋值中
a,b=1,2
  1. 在可变长度参数中
def add(*arg) #arg is a tuple
    return sum(arg)
于 2020-08-19T06:33:27.527 回答