Item 6: Prefer Multiple Assignment Unpacking Over Indexing

Python has a built-in tuple type that can be used to create immutable, ordered sequences of values. In the simplest case, a tuple is a pair of two values, such as keys and values from a dictionary:

Python有一个内置的元组类型,可以用来创建不可变的、有序的值序列。在最简单的情况下,元组是一对值,比如字典的键值对:

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

The values in tuples can be accessed through numerical indexes:
元组中的值可以通过数值索引访问:

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

Once a tuple is created, you can’t modify it by assigning a new value to an index:
一旦元组被创建,你就不能通过给索引赋新值来修改它:

pair = ( 'Chocolate ', 'Peanut butter ')  
pair [0] = 'Honey '  
 
>>> 
Traceback ...  
TypeError: 'tuple ' object does not support item assignment

Python also has syntax for unpacking, which allows for assigning multiple values in a single statement. The patterns that you specify in unpacking assignments look a lot like trying to mutate tuples—which isn’t allowed— but they actually work quite differently. For example, if you know that a tuple is a pair, instead of using indexes to access its values, you can assign it to a tuple of two variable names:

Python的解包语法,允许它在一条语句中给多个变量同时赋值。解包赋值的模式看起来很像试图改变元组(这是不允许的),但它们实际上的运行原理非常不同。例如,如果你知道一个元组是一对,你可以将它赋值给两个变量名,而不是使用索引来访问它的值:

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

Unpacking has less visual noise than accessing the tuple’s indexes, and it often requires fewer lines. The same pattern matching syntax of unpacking works when assigning to lists, sequences, and multiple levels of arbitrary iterables within iterables. I don’t recommend doing the following in your code, but it’s important to know that it’s possible and how it works:

解包比访问元组的索引具有更少的视觉干扰,而且通常需要更少的行。当给列表、序列和可迭代对象中任意级别的可迭代对象赋值时,解包的匹配语法模式也同样适用。我不建议在你的代码中做以下事情,但重要的是你要知道这是可能的,以及它是如何工作的:

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  

Newcomers to Python may be surprised to learn that unpacking can even be used to swap values in place without the need to create temporary variables. Here, I use typical syntax with indexes to swap the values between two positions in a list as part of an ascending order sorting algorithm:

刚接触Python的人可能会惊讶地发现,解包甚至可以用于交换值,而不需要创建临时变量。这里,作为升序排序算法的一部分,我使用典型的索引语法来交换列表中两个位置的值:

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 ']  

However, with unpacking syntax, it’s possible to swap indexes in a single line:
然而,使用解包语法,可以在一行中交换索引:

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 ']  

The way this swap works is that the right side of the assignment (a [i], a[i-1]) is evaluated first, and its values are put into a new temporary, unnamed tuple (such as ( 'carrots ', 'pretzels ') on the first iteration of the loops). Then, the unpacking pattern from the left side of the assignment (a [i-1], a[i]) is used to receive that tuple value and assign it to the variable names a [i-1] and a[i], respectively. This replaces 'pretzels ' with 'carrots ' at index 0 and 'carrots ' with 'pretzels' at index 1. Finally, the temporary unnamed tuple silently goes away.

这种交换的工作方式是首先计算赋值的右侧(a [i], a[i-1]),并将其值放入一个新的临时的、未命名的元组(例如('carrot ', 'pretzels ')在循环的第一次迭代中)。然后,使用赋值左边的解包模式(a [i-1], a[i])来接收该元组值,并将其分别赋给变量名a[i -1]和a[i]。在索引0中将“pretzels”替换为“carrots”,在索引1中将“carrots”替换为“pretzels”。最后,临时的未命名元组悄然消失。

Another valuable application of unpacking is in the target list of for loops and similar constructs, such as comprehensions and generator expressions (see Item 27: “Use Comprehensions Instead of map and filter” for those). As an example for contrast, here I iterate over a list of snacks without using unpacking:

解包的另一个有价值的应用是在for循环和类似结构的目标列表中,比如推导式和生成器表达式(参见第27项:“使用推导式代替map和filter”)。作为对比的例子,我在这里不使用解包的方式迭代一个零食列表:

snacks = [ ( 'bacon ', 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: bacon has 350 calories  
#2: donut has 240 calories  
#3: muffin has 190 calories  

This works, but it’s noisy. There are a lot of extra characters required in order to index into the various levels of the snacks structure. Here, I achieve the same output by using unpacking along with the enumerate built-in function (see Item 7: “Prefer enumerate Over range”) :

这招管用,但有点嘈杂。为了索引零食结构的不同层次,需要添加许多额外的字符。这里,我通过使用解包和enumerate内置函数来实现相同的输出(参见第7项:“使用enumerate而不是range”):

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

This is the Pythonic way to write this type of loop; it’s short and easy to understand. There’s usually no need to access anything using indexes.

这是python编写这种类型循环的方法; 它很短并且很容易理解,通常不需要使用索引访问任何东西。

Python provides additional unpacking functionality for list construction (see Item 13: “Prefer Catch-All Unpacking Over Slicing”), function arguments (see Item 22: “Reduce Visual Noise with Variable Positional Arguments”), keyword arguments (see Item 23: “Provide Optional Behavior with Keyword Arguments”), multiple return values (see Item 19: “Never Unpack More Than Three Variables When Functions Return Multiple Values”), and more.

Python为列表提供了额外的解包功能(参见第13项:" 使用全集解包而不是切片")、函数参数(参见第22项:"使用可变位置参数减少视觉噪声")、关键字参数(参见第23项:"使用关键字参数提供可选行为")、多个返回值(参见第19项:“当函数返回多个值时,不要解包超过三个变量”)等等。

Using unpacking wisely will enable you to avoid indexing when possible, resulting in clearer and more Pythonic code.

明智地使用解包将使您能够在可能的情况下避免索引,从而生成更清晰、更符合python风格的代码。

Things to Remember
要记住的事

✦ Python has special syntax called unpacking for assigning multiple values in a single statement.
✦ Unpacking is generalized in Python and can be applied to any iterable, including many levels of iterables within iterables.
✦ Reduce visual noise and increase code clarity by using unpacking to avoid explicitly indexing into sequences.

✦ Python有特殊的解包语法,用于在单行语句中分配多个值。
✦ 解包在Python中很通用,可以应用于任何可迭代对象,包括可迭代对象中的嵌套的多级别可迭代对象。
✦ 通过使用解包而不是显示地使用序列索引,以减少视觉噪音,增加代码清晰度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353