2.3 元组不仅仅是不可变的列表

元祖拆包:

元组拆包可以应用到任何可迭代对象上,唯一的硬性要求是,被可迭代对象中的元素数量必须要跟接受这些元素的元组的空档数一致。除非我们用 * 来表示忽略多余的元素。

最好辨认的元组拆包形式就是平行赋值,也就是说把一个可迭代对象里 的元素,一并赋值到由对应的变量组成的元组中:

>>> lax_coordinates = (33.9425, -118.408056) 
>>> latitude, longitude = lax_coordinates # 元组拆包 
>>> latitude 
33.9425 
>>> longitude 
-118.408056

另外一个很优雅的写法当属不使用中间变量交换两个变量的值:

>>> b, a = a, b

还可以用 * 运算符把一个可迭代对象拆开作为函数的参数:

>>> divmod(20, 8) 
(2, 4) 
>>> t = (20, 8) 
>>> divmod(*t) 
(2, 4)

在进行拆包的时候,我们不总是对元组里所有的数据都感兴趣,_ 占位符能帮助处理这种情况,下面这段代码展示了它的用法:

>>> import os 
>>> _, filename = os.path.split('/home/luciano/.ssh/idrsa.pub') 
>>> filename 
'idrsa.pub'

用*来处理剩下的元素:

在 Python 中,函数用 *args 来获取不确定数量的参数算是一种经典写法了。

于是 Python 3 里,这个概念被扩展到了平行赋值中:

>>> a, b, *rest = range(5) 
>>> a, b, rest 
(0, 1, [2, 3, 4]) 
>>> a, b, *rest = range(3) 
>>> a, b, rest 
(0, 1, [2]) 
>>> a, b, *rest = range(2) 
>>> a, b, rest 
(0, 1, [])

在平行赋值中,* 前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:

>>> a, *body, c, d = range(5) 
>>> a, body, c, d 
(0, [1, 2], 3, 4) 
>>> *head, b, c, d = range(5) 
>>> head, b, c, d 
([0, 1], 2, 3, 4)

嵌套元组拆包:

metro_areas = [
    ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
    ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
]
for name, cc, pop, (latitude, longitude) in metro_areas:
    print(name, latitude, longitude)

具名元组 :

collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类——这个带名字的类对调试程序有很大帮助。

>>> from collections import namedtuple 
>>> City = namedtuple('City', 'name country population coordinates')  ➊ 
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))  ➋ 
>>> tokyo 
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667)) 
>>> tokyo.population  ➌ 
36.933 
>>> tokyo.coordinates 
(35.689722, 139.691667) 
>>> tokyo[1] 
'JP'

❶ 创建一个具名元组需要两个参数,一个是类名,另一个是类的各个字段的名字。后者可以是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串。

❷ 存放在对应字段里的数据要以一串参数的形式传入到构造函数中 (注意,元组的构造函数却只接受单一的可迭代对象)。

❸ 你可以通过字段名或者位置来获取一个字段的信息。

除了从普通元组那里继承来的属性之外,具名元组还有一些自己专有的属性:_fields 类属性、类方法 _make(iterable) 和实例方法 _asdict()。 

>>> City._fields  ➊ 
('name', 'country', 'population', 'coordinates') 
>>> LatLong = namedtuple('LatLong', 'lat long') 
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889)) >>> delhi = City._make(delhi_data)  ➋ 
>>> delhi._asdict()  ➌ 
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))]) 
>>> for key, value in delhi._asdict().items():
 >>>       print(key + ':', value)
name: Delhi NCR 
country: IN 
population: 21.935 
coordinates: LatLong(lat=28.613889, long=77.208889)

❶ _fields 属性是一个包含这个类所有字段名称的元组。

❷ 用 _make() 通过接受一个可迭代对象来生成这个类的一个实例,它 的作用跟 City(*delhi_data) 是一样的。

❸ _asdict() 把具名元组以 collections.OrderedDict 的形式返回,并不是返回dict,只是可以利用它来把元组里的信息友好地呈现出来。

元祖的排序:

Python中的列表是可变对象,对可变对象可以排序,比如说:L.sort()或者sorted(L),但在元祖数据结构中,因为元祖是不可变对象,不会提供列表中的这些方法,一个方法就是先将元祖转变为列表,对转变后的列表list排序后,再转变回元祖tuple:

>>> T = (11, 33, 22, 44)     # 元祖对象
>>> L = list(T)              # 转变为列表对象
>>> L.sort()                 # 用sort()函数对列表进行排序
>>> L                        # 排序后的顺序
[11, 22, 33, 44]
>>> T = tuple(L)             # 再将列表转换回元祖
>>> T
(11, 22, 33, 44)

第二个方法就是借助sorted()方法,它适用于所有的可迭代序列,但返回的是一个列表list,所以排序后还是要手动转换成元祖tuple:

>>> t = (1,2,3)
>>> t = sorted(t, reverse=True)
>>> print(t)
>>> t = tuple(t)
>>> print(t)
[3, 2, 1]
(3, 2, 1)
posted @ 2019-03-31 10:26  皮皮嘉  阅读(293)  评论(0编辑  收藏  举报