首页 > Python基础教程 >
-
Python 官方文档 中文版 5. Data Structures(2)
如你所见,在输出时,元组总是被圆括号括起来,这样就可以更准确地解释嵌套的元组。元组可能在输入时带有括号,也可能不带括号,尽管通常来说圆括号是必要的(如果元组是一个更大的表达式中的一部分)。不能将一个值赋给元组中的独立元素,但是可以创建带有可修改对象(如列表)的元组。
尽管 tuple
可能看上去和 list
很像,但是它们经常用于不同的场景和不同的目的。元组是不可修改的,通常包含一个序列的异构(heterogeneous)的元素,这些元素通过解包(见本节后面的内容)或索引来访问(在 namedtuples
中,甚至可以通过属性来访问)。list
是可修改的,list
的元素通常是同构的(homogeneous),并通过遍历 list
来访问。
构建包含0个或1个元素的元组是一个特别的问题:Python 语法对此有额外的兼容来适应这个问题。空元组通过一对圆括号来构建;只有一个元素的元组通过跟随着一个逗号的值来构建(仅用括号括起一个值是不够的)。有点丑,但是很高效。如:
>>> empty = ()
>>> singleton = 'hello', # <-- 注意最后的逗号
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
语句 t = 12345, 54321, 'hello!'
是 tuple packing
的一个例子:12345
,54321
和 'hello!'
被 pack
在了一个元组里。相反的操作同样可行:
>>> x, y, z = t
这被称为 sequence unpacking
,这对右边的任何 sequence
都起作用。sequence unpacking
要求等号左边的变量个数与 sequence
中的元素个数相同。注意,多重赋值(multiple assignment)其实就是 tuple packing
和 sequence unpacking
的组合。
5.4. 集合 ✔
Python 还支持 set
。set
是一种没有重复元素的无序集合。基本用法有 membership testing 和消除重复项。set
对象支持一些数学运算,如并、交、差和对称差。
可以用花括号或 set()
函数来创建 set
。注意,如果要创建一个空的 set
,必须使用set()
,而不是{}
,后者会创建一个空的字典(一种我们下一节会讨论的数据结构)。
这里有些简短的示例:
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # fast membership testing
True
>>> 'crabgrass' in basket
False
>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # letters in both a and b
{'a', 'c'}
>>> a ^ b # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}
与列表推导式类似,set
推导式也被 Python 支持:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
5.5. 字典 ✔
另一种 Python 中有用的内置数据类型是字典(见Mapping Types — dict)。字典在其他语言中有时被称为联合存储器(associative memories)或者关联数组(“associative arrays”)。字典不像 sequance 那样用一个范围的数字作为索引,而是使用 key 作为索引,key 可以是不可修改的类型;字符串和数字都可以作为 key。元组如果只包含字符串、数字或者元组,就可以作为 key 来使用;元组中如果直接或间接包含可修改的对象,就不能作为 key 来使用。不能用 list 作为字典,因为 list 可以通过索引赋值、切片赋值或者 append() 和 extend() 这样的方法来修改。
最好将字典看作一个键值对的集合,并要求键都是唯一的(在一个字典内)。空字典通过一对花括号({}
)来创建。在花括号中输入用逗号分隔键值对的列表,可以为字典添加初始键值对;这也是字典在输出中打印的方式。
字典中的主要操作是通过 key 来存储 value 和提取 value。可以用 del 删除键值对。如果你用一个已经存在的 key 来存储值,那么原来的 value 会被覆盖。用一个不存在 key 来提取 value 会产生错误。
对字典使用 list(d)
会返回包含字典中所有 key 的列表,列表中元素顺序为插入时的顺序(如果你希望它是被排序过的,就替换成 sorted(d)
)。使用 in 关键字来检查一个 key 是否存在于字典中。
字典的简单示例:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
dict()
通过键值对序列来构建字典:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
另外,字典推导式可以通过任意键和值的表达式来创建字典
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
当 key 是简单的字符串时,用关键字参数来指定键值对会更加简单:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
5.6. 循环技巧 ✔
在遍历字典时,使用字典的 items()
方法可以同时将字典中的 key
以及对应的 value
提取出来。
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
当遍历一个 sequence
时,使用 enumerate()
函数可以同时将索引和对应的值提取出来。
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
想要同时循环两个或多个序列,可以将序列与 zip() 函数一起使用。
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
想要倒序遍历一个 sequence
,可以先指定正向的 sequence
,然后调用 reversed()
函数。
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
想要以排好序的顺序遍历一个 sequence
,用 sorted()
函数。该函数返回一个排好序的 list
,保持原始对象不变。
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
有时你在遍历一个 list
的过程中,你会很想要改变这个 list
;然而,通常情况下更简单和安全的方式是创建一个新的 list
。
>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
... if not math.isnan(value):
... filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
5.7. More on Conditions ✔
while
语句和 if
语句中使用的条件可以包含任何运算符,而不仅仅是比较运算符。
比较运算符 in
和 not in
检查一个值是否存在于一个 sequence
中。is
和 is not
操作符比较两个对象是否是相同的对象;这只适用于像 list
这样的可修改对象。所有的比较运算符有相同的优先级,比较运算符的优先级低于数值运算符
比较运算符可以被链接(chained)起来。比如,a < b == c
检查 a
是否小于 b
,且 b
是否等于 c
。
比较运算可以通过布尔运算符 and
和 or
来合并在一起,比较运算(或者其他布尔表达式)的结果可以使用 not
运算符进行否定。and
、or
和 not
的优先级低于比较运算符;在 and
、or
和 not
中,not
的优先级最高,or
最低,所以 A and not B or C
等价于 (A and (not B) or C)
。通常,圆括号可以用于明确所需的组合。
布尔运算符 and 和 or 属于短路(short-circuit)运算符:短路运算符的计算从左往右进行,并且,一旦结果确定,就停止计算。比如,如果 A
和 C
的值为 True
,但 B
为 False
,表达式 A and B and C
就不会再计算 C
。当作为普通值而不是布尔值时,短路运算符的返回值为最后一个计算的操作数。
可以将比较运算或者其他布尔表达式的结果赋值给一个变量。比如:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
Python 与 C 不同,Python 表达式内的赋值必须使用 walrus operator
:=
进行。这能避免在 C 中经常遇到的一类问题:在表达式中本该使用 ==
的地方,却使用了 =
。
5.8. 比较 Sequence 和其他类型
sequence 对象可以与其他具有相同 sequence 类型的对象进行比较。比较通过字典顺序(lexicographical order)进行:首先比较两个序列的第一项,如果不同,那么就可以确定比较结果;如果相同,就比较两个序列的下一项,如此类推,直到其中一个序列的元素被比较完为止。如果进行比较的两个项,本身就是同一类型的 sequence
,则递归地进行字典顺序的比较。如果比较下来,两个序列中所有的项都相等,那么就认为这两个序列相等。如果一个序列是另一个序列的初始子序列(initial sub-sequence),较短的序列是较小的那个。字符串的字典顺序比较使用 Unicode code point number 来排序单个字符。一些同类型序列之间进行比较的例子:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
注意,将不同类型的对象进行 <
或 >
比较是合法的,前提是这些对象具有恰当的比较方法。比如,mixed numeric type 通过它们的 numeric value 进行比较,因此 0 等同于 0.0。否则,解释器会抛出一个 TypeError
异常,而不是提供一个任意排序的结果。
脚注
Other languages may return the mutated object, which allows method chaining, such as d->insert("a")->remove("b")->sort();