元组有什么用处?
我现在正在上Python课,最近我们学习了元组这种数据类型。我看了维基百科上的介绍,但还是搞不清楚这种数据类型在实际中有什么用。能不能给我一些例子,最好是用Python来说明,说明一下为什么有时候需要一个不可改变的数字集合?这和列表有什么不同呢?
12 个回答
理解这个概念的最好方式是:
- 元组是一种没有字段名称的记录。
- 当你不想指定字段名称时,就可以使用元组来代替记录。
所以,不用写这样的代码:
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)
这突出了特定类型的元组总是应该有固定数量的字段,并且这些字段的顺序也是固定的,每个字段通常都是同一种类型。在这个例子中,一个人总是由两个字段组成,一个是字符串,另一个是整数。
这与列表形成鲜明对比,列表的长度是可变的(每个列表中的项目数量通常不同,你可以写函数来添加和删除项目),而列表中的每个项目通常是同一种类型。例如,你会有一个人列表和一个地址列表——你不会把人和地址混在同一个列表里。而在同一个元组中混合不同类型的数据正是元组的意义所在。元组中的字段通常是不同类型的(但并不总是,比如你可以有一个(Float, Float, Float)
的元组来表示x、y、z坐标)。
元组和列表通常是嵌套的。有一个元组列表是很常见的。你可以有一个人元组的列表,也可以有一个人对象的列表。你还可以有一个字段的值是一个列表。例如,如果你有一个地址簿,一个人可以有多个地址,你可以有一个类型为(Person, [String])
的元组。[String]
类型在函数式编程语言中通常用来表示字符串列表。在Python中,你不需要写出类型,但你可以以相同的方式使用元组,把一个Person
对象放在元组的第一个字段,把字符串列表放在第二个字段。
在Python中,容易产生混淆,因为这个语言并不强制执行静态类型语言中编译器强制的这些规则。在那些语言中,你不能混合不同类型的元组。例如,你不能从一个声明返回(String, Integer)
元组的函数中返回一个(String, String)
元组。如果类型声明说你计划返回一个元组,你也不能返回一个列表,反之亦然。列表严格用于可增长的项目集合,而元组则严格用于固定大小的记录。如果你愿意,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是最友好的)。这些语言的设计者认为不可变性非常有用,以至于所有列表都是不可变的。(与其改变一个列表来添加或删除项目,不如创建一个新列表,添加或删除项目。不可变性保证了旧的列表副本永远不会改变,因此编译器和运行时可以通过在新列表中重用旧列表的部分并在不再需要时进行垃圾回收来使代码性能良好。)
元组在你需要把多个数据组合成一个键的时候,非常适合用作字典的键,而且你不想为此专门创建一个类。
a = {}
a[(1,2,"bob")] = "hello!"
a[("Hello","en-US")] = "Hi There!"
我主要是用这个功能来创建一个字典,字典的键是网格顶点的坐标。不过,在我的情况下,涉及到的浮点数的精确比较效果很好,但这不一定适合你们的情况【如果不行的话,我可能会把你们的浮点数转换成某种固定点的整数】
元组(Tuples)在你想从一个函数返回多个结果时非常有用。
因为元组是不可改变的,所以它们可以用作字典的键(而列表是不能的)。