编写高质量Python(第6条) 把数据结构拆分到多个变量里,不要专门通过下标访问

第6条 把数据结构拆分到多个变量里,不要专门通过下标访问

​ Python 内置的元祖类型可以创建不可变的序列,把很多元素依次保存起来。最简单的方法是只用元祖保存两个值。例如字典里面的键值对。

snack_calories = ({
    'chips': 140,
    'popcorn': 80,
    'nuts': 190,
})
items = tuple(snack_calories.items())
print(items)

>>>
(('chips', 140), ('popcorn', 80), ('nuts', 190))

​ 我们也可以用整数作为下标,通过下标来访问元祖里面对应的元素。

items = ('Peanut butter', 'Jelly')
first = items[0]
second = items[1]
print(first, 'and', second)

>>>
Peanut butter and Jelly

​ 创建好 tuple 之后,就不能通过下标给其中的元素赋值了。

pairs = ('Chocolate', "Peanut butter")
pairs[0] = 'Honey'

>>>
Traceback ...
TypeError: 'tuple' object does not support item assignment

​ Python 还有一种写法,叫做拆分(unpacking)。这种写法让我们只用一条语句,就可以把元祖里面的元素赋值给多个变量。对元祖做 unpacking 的时候,并不是修改它的元素。元组的元素本身并不能修改,但是可以通过 unpacking 把这些元素分布赋给相应的变量,那些变量可以修改的。

​ 例如,若我们确定某个元组里面只有两个元素,那么就可以把这两个元素分别赋值给相应的变量,而不用再通过下标进行访问。

item = ('Peanut butter', 'Jelly')
first, second = item
print(first, 'and', second)

>>>
Peanut butter and Jelly

通过 unpacking 来赋值要比通过下标去访问元组内的元素更清晰,而且这种写法所需的代码量通常比较少。 当然,赋值操作的左边除了可以罗列单个变量,也可以写成列表、序列或任意深度的可迭代对象。

​ 例如,下面这种写法就是成立的,然而不推荐大家在代码里面这么写。

favorite_snacks = {
    'salty': ('pretzels', 100),
    'sweet': ('cookies', 180),
    'veggie': ('carrots', 20),
}

((type1, (name1,cals1)),
 (type2, (name2,cals2)),
 (type3, (name3,cals3))) = favorite_snacks.items()

print(f'Favorite {type1} is {name1} with {cals1} calories')
print(f'Favorite {type2} is {name2} with {cals2} calories')
print(f'Favorite {type3} is {name3} with {cals3} calories')

>>>
Favorite salty is pretzels with 100 calories
Favorite sweet is cookies with 180 calories
Favorite veggie is carrots with 20 calories

​ Python 新手可能还不知道,其实我们可以通过 unpacking 原地交换两个变量,而不用专门创建这个临时变量。先来看下面这个传统的写法,这个升序算法用下标的形式来访问有待排序的这个 a 列表,并且通过变量交换其中两个相邻的元素。

def bubble_sort(a):
	for _ in range(len(a)):
		for i in range(1, len(a)):
			if a[i] < a[i - 1]:
				temp = a[i]
				a[i] = a[i - 1]
				a[i - 1] = temp
      
names = ['pretzels', 'carrots', 'arugula', 'bacon']
bubble_sort(names)
print(names)

>>>
['arugula', 'bacon', 'carrots', 'pretzels']

​ 有了 unpacking 机制之后,只需要写一行代码就可以交换这两个元素,而不用像刚才那样分成三行来写。

def bubble_sort(a):
	for _ in range(len(a)):
		for i in range(1, len(a)):
			if a[i] < a[i - 1]:
				a[i - 1], a[i] = a[i], a[i - 1] # swap
      
names = ['pretzels', 'carrots', 'arugula', 'bacon']
bubble_sort(names)
print(names)

>>>
['arugula', 'bacon', 'carrots', 'pretzels']

​ 这样写为什么可以成立呢?因为 unpacking 处理赋值操作的时候,首先对 = 号右侧求值,于是,它会新建一个临时的元组,把a[i] 和a[i - 1]这两个元素放到这个元组里面。例如,头一次进入内部的 for 循环时,这两个元素分别是 ‘carrots’ 与 ‘pretzels’, 于是,系统就会创建出(‘carrots’, ‘pretzels’)这样一个临时的元组。然后,Python 会对这个临时的元组做 unpacking, 把里面的两个元素放在 = 号左侧的那两个地方, 于是,‘carrots’ 就会把 a[i - 1] 里面原有的 'pretzels’换掉,‘pretzels’ 也会把 a[i] 里面原有的 ‘carrots’ 换掉。现在,出现在a[0]这个位置上面的字符串是 ‘pretzels’。做完 unpacking 后,系统就会扔掉这个临时的元组。

​ unpacking 机制还有一个特别重要的用法,就是可以在 for 循环或者类似的结构 (例如推导表达式中,详情参考第27条) 里面,把复杂的数据拆分到相关的变量之中。

​ 下面这段代码没有使用 unpacking 机制,而是采用传统的写法来迭代 snacks 列表里面的元素。

snacks = [('bacons', 350), ('donut', 240), ('muffin',190)]
for i in range(len(snacks)):
	item = snacks[i]
	name = item[0]
	calories = item[1]
	print(f'#{i+1}:{name} has {calories} calories')
  
>>>
#1:bacons has 350 calories
#2:donut has 240 calories
#3:muffin has 190 calories

​ 这样写确实没错,但看起来很乱,因为 snakcs 结构并不是一个简单的列表, 它的每个元素都是一个元组,所以必须逐层访问才能查到最为具体的数据,也就是每种零食的名称和卡路里。下面换一种写法,首先内置的 enumerate 函数 (参见第7条)获得当前要迭代的元组,然后针对这个元组做 unpacking,这样就可以直接得到具体的 name 与 calories 值了。

for rank, (name, calories) in enumerate(snacks, 1):
	print(f'#{rank}: {name} has {calories} calories')
  
>>>
#1: bacons has 350 calories
#2: donut has 240 calories
#3: muffin has 190 calories

​ Python 的 unpacking 机制可以用在很多方面,例如构建列表 (参见第13条)、给函数值设计列表(参见第22条)、传递关键字参数(参见第23条)、接受多个返回值(参见第19条)等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值