简体   繁体   English

解包、扩展解包和嵌套扩展解包

[英]Unpacking, extended unpacking and nested extended unpacking

Consider the following expressions.考虑以下表达式。 Note that some expressions are repeated to present the "context".请注意,某些表达式被重复以呈现“上下文”。

(this is a long list) (这是一个很长的清单)

a, b = 1, 2                          # simple sequence assignment
a, b = ['green', 'blue']             # list asqignment
a, b = 'XY'                          # string assignment
a, b = range(1,5,2)                  # any iterable will do


                                     # nested sequence assignment

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z' 

(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack


                                     # extended sequence unpacking

a, *b = 1,2,3,4,5                    # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5                    # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5                 # a = 1, b = [2,3,4], c = 5

a, *b = 'X'                          # a = 'X', b = []
*a, b = 'X'                          # a = [], b = 'X'
a, *b, c = "XY"                      # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

a, b, *c = 1,2,3                     # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3                  # a = 1, b = 2, c = 3, d = []

a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment

(a,b), c = [1,2],'this'              # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this'             # a = '1', b = '2', c = ['this']

(a,b), c, *d = [1,2],'this'          # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this'          # a = '1', b = '2', c = [], d = 'this'

(a,b), (c, *d) = [1,2],'this'        # a = '1', b = '2', c = 't', d = ['h', 'i', 's']

*a = 1                               # ERROR -- target must be in a list or tuple
*a = (1,2)                           # ERROR -- target must be in a list or tuple
*a, = (1,2)                          # a = [1,2]
*a, = 1                              # ERROR -- 'int' object is not iterable
*a, = [1]                            # a = [1]
*a = [1]                             # ERROR -- target must be in a list or tuple
*a, = (1,)                           # a = [1]
*a, = (1)                            # ERROR -- 'int' object is not iterable

*a, b = [1]                          # a = [], b = 1
*a, b = (1,)                         # a = [], b = 1

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
(a,b), *c = 1,2,3                    # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]


                                     # extended sequence unpacking -- NESTED

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

*(a,b) = 1,2                         # ERROR -- target must be in a list or tuple
*(a,b), = 1,2                        # a = 1, b = 2

*(a,b) = 'XY'                        # ERROR -- target must be in a list or tuple
*(a,b), = 'XY'                       # a = 'X', b = 'Y'

*(a, b) = 'this'                     # ERROR -- target must be in a list or tuple
*(a, b), = 'this'                    # ERROR -- too many values to unpack
*(a, *b), = 'this'                   # a = 't', b = ['h', 'i', 's']

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

*(a,*b), = 1,2,3,3,4,5,6,7           # a = 1, b = [2, 3, 3, 4, 5, 6, 7]

*(a,*b), *c = 1,2,3,3,4,5,6,7        # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7     # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7         # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY'      # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']

*(a,*b), c, d = 1,2,3,3,4,5,6,7      # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7    # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7   # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7   # ERROR -- two starred expressions in assignment


*(a,b), c = 'XY', 3                  # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3                 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3                   # a = 'X', b = 'Y', c = 3

*(a,b), c = 'XY', 3, 4               # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4              # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4                # ERROR -- too many values to unpack

How to correctly deduce the result of such expressions by hand?如何手动正确推导出此类表达式的结果?

My apologies for the length of this post, but I decided to opt for completeness.对于这篇文章的长度,我深表歉意,但我决定选择完整性。

Once you know a few basic rules, it's not hard to generalize them.一旦你知道了一些基本规则,就不难概括它们。 I'll do my best to explain with a few examples.我会尽力用几个例子来解释。 Since you're talking about evaluating these "by hand," I'll suggest some simple substitution rules.由于您正在谈论“手动”评估这些,我将建议一些简单的替换规则。 Basically, you might find it easier to understand an expression if all the iterables are formatted in the same way.基本上,如果所有可迭代对象的格式都相同,您可能会发现更容易理解表达式。

For the purposes of unpacking only, the following substitutions are valid on the right side of the = (ie for rvalues ):仅出于解包的目的,以下替换在=的右侧有效(即对于rvalues ):

'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')

If you find that a value doesn't get unpacked, then you'll undo the substitution.如果您发现一个值没有被解包,那么您将撤消替换。 (See below for further explanation.) (有关进一步说明,请参见下文。)

Also, when you see "naked" commas, pretend there's a top-level tuple.此外,当您看到“裸”逗号时,请假装有一个顶级元组。 Do this on both the left and the right side (ie for lvalues and rvalues ):在左侧和右侧执行此操作(即对于lvaluesrvalues ):

'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)

With those simple rules in mind, here are some examples:考虑到这些简单的规则,以下是一些示例:

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'

Applying the above rules, we convert "XY" to ('X', 'Y') , and cover the naked commas in parens:应用上述规则,我们将"XY"转换为('X', 'Y') ,并覆盖括号中的裸逗号:

((a, b), c) = (('X', 'Y'), 'Z')

The visual correspondence here makes it fairly obvious how the assignment works.这里的视觉对应使得分配的工作方式相当明显。

Here's an erroneous example:这是一个错误的例子:

(a,b), c = "XYZ"

Following the above substitution rules, we get the below:遵循上述替换规则,我们得到以下结果:

((a, b), c) = ('X', 'Y', 'Z')

This is clearly erroneous;这显然是错误的; the nested structures don't match up.嵌套结构不匹配。 Now let's see how it works for a slightly more complex example:现在让我们看看它是如何处理一个稍微复杂的例子的:

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'

Applying the above rules, we get应用上述规则,我们得到

((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))

But now it's clear from the structure that 'this' won't be unpacked, but assigned directly to c .但是现在从结构中可以清楚地看出'this'不会被解包,而是直接分配给c So we undo the substitution.所以我们撤消了替换。

((a, b), c) = ((1, 2), 'this')

Now let's see what happens when we wrap c in a tuple:现在让我们看看当我们将c包装在一个元组中时会发生什么:

(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack

Becomes成为

((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))

Again, the error is obvious.同样,错误是显而易见的。 c is no longer a naked variable, but a variable inside a sequence, and so the corresponding sequence on the right is unpacked into (c,) . c不再是一个裸变量,而是一个序列内的变量,因此右边的相应序列被解包到(c,) But the sequences have a different length, so there's an error.但是序列有不同的长度,所以有一个错误。

Now for extended unpacking using the * operator.现在使用*运算符进行扩展解包。 This is a bit more complex, but it's still fairly straightforward.这有点复杂,但它仍然相当简单。 A variable preceded by * becomes a list, which contains any items from the corresponding sequence that aren't assigned to variable names.*开头的变量成为一个列表,其中包含相应序列中未分配给变量名称的所有项目。 Starting with a fairly simple example:从一个相当简单的例子开始:

a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

This becomes这变成

(a, *b, c) = ('X', '.', '.', '.', 'Y')

The simplest way to analyze this is to work from the ends.分析这一点的最简单方法是从两端开始工作。 'X' is assigned to a and 'Y' is assigned to c . 'X'分配给a并且'Y'分配给c The remaining values in the sequence are put in a list and assigned to b .序列中的其余值放在一个列表中并分配给b

Lvalues like (*a, b) and (a, *b) are just special cases of the above.(*a, b)(a, *b)这样的左值只是上述情况的特例。 You can't have two * operators inside one lvalue sequence because it would be ambiguous.一个左值序列中不能有两个*运算符,因为它会产生歧义。 Where would the values go in something like this (a, *b, *c, d) -- in b or c ?像这样(a, *b, *c, d)的值会在哪里——在bc I'll consider the nested case in a moment.稍后我将考虑嵌套的情况。

*a = 1                               # ERROR -- target must be in a list or tuple

Here the error is fairly self-explanatory.这里的错误是不言自明的。 The target ( *a ) must be in a tuple.目标 ( *a ) 必须在元组中。

*a, = (1,2)                          # a = [1,2]

This works because there's a naked comma.这是有效的,因为有一个裸逗号。 Applying the rules...应用规则...

(*a,) = (1, 2)

Since there are no variables other than *a , *a slurps up all the values in the rvalue sequence.由于除*a之外没有其他变量,因此*a会提取右值序列中的所有值。 What if you replace the (1, 2) with a single value?如果用单个值替换(1, 2)怎样?

*a, = 1                              # ERROR -- 'int' object is not iterable

becomes变成

(*a,) = 1

Again, the error here is self-explanatory.同样,这里的错误是不言自明的。 You can't unpack something that isn't a sequence, and *a needs something to unpack.你不能解压一些不是序列的东西,而*a需要解压一些东西。 So we put it in a sequence所以我们把它放在一个序列中

*a, = [1]                            # a = [1]

Which is eqivalent to相当于

(*a,) = (1,)

Finally, this is a common point of confusion: (1) is the same as 1 -- you need a comma to distinguish a tuple from an arithmetic statement.最后,这是一个常见的混淆点: (1)1相同——您需要一个逗号来区分元组和算术语句。

*a, = (1)                            # ERROR -- 'int' object is not 

Now for nesting.现在用于嵌套。 Actually this example wasn't in your "NESTED" section;实际上这个例子不在你的“NESTED”部分; perhaps you didn't realize it was nested?也许你没有意识到它是嵌套的?

(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]

Becomes成为

((a, b), *c) = (('X', 'Y'), 2, 3)

The first value in the top-level tuple gets assigned, and the remaining values in the top-level tuple ( 2 and 3 ) are assigned to c -- just as we should expect.顶级元组中的第一个值被分配,顶级元组中的其余值( 23 )被分配给c —— 正如我们所期望的那样。

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

I've already explained above why the first line throws an error.我已经在上面解释了为什么第一行会抛出错误。 The second line is silly but here's why it works:第二行很愚蠢,但这就是它起作用的原因:

(*(a, b), c) = (1, 2, 3)

As previously explained, we work from the ends.如前所述,我们从头开始工作。 3 is assigned to c , and then the remaining values are assigned to the variable with the * preceding it, in this case, (a, b) . 3分配给c ,然后将其余值分配给前面带有*的变量,在本例中为(a, b) So that's equivalent to (a, b) = (1, 2) , which happens to work because there are the right number of elements.所以这等效于(a, b) = (1, 2) ,这恰好有效,因为有正确数量的元素。 I can't think of any reason this would ever appear in working code.我想不出任何原因会出现在工作代码中。 Similarly,相似地,

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

becomes变成

(*(a, *b), c) = ('t', 'h', 'i', 's')

Working from the ends, 's' is assigned to c , and ('t', 'h', 'i') is assigned to (a, *b) .从两端开始, 's'分配给c('t', 'h', 'i')分配给(a, *b) Working again from the ends, 't' is assigned to a , and ('h', 'i') is assigned to b as a list.从末端再次工作, 't'被分配给a('h', 'i')被分配给 b 作为列表。 This is another silly example that should never appear in working code.这是另一个不应出现在工作代码中的愚蠢示例。

I find the Python 2 tuple unpacking pretty straightforward.我发现 Python 2 元组的解包非常简单。 Each name on the left corresponds with either an entire sequence or a single item in a sequence on the right.左侧的每个名称对应于整个序列或右侧序列中的单个项目。 If names correspond to single items of any sequence, then there must be enough names to cover all of the items.如果名称对应于任何序列的单个项目,则必须有足够的名称来覆盖所有项目。

Extended unpacking, however, can certainly be confusing, because it is so powerful.然而,扩展解包肯定会令人困惑,因为它非常强大。 The reality is you should never be doing the last 10 or more valid examples you gave -- if the data is that structured, it should be in a dict or a class instance, not unstructured forms like lists.现实情况是,你永远不应该做你给出的最后 10 个或更多有效示例——如果数据是结构化的,它应该在dict或类实例中,而不是像列表这样的非结构化形式。

Clearly, the new syntax can be abused.显然,新语法可能会被滥用。 The answer to your question is that you shouldn't have to read expressions like that -- they're bad practice and I doubt they'll be used.你的问题的答案是你不应该阅读这样的表达——它们是不好的做法,我怀疑它们会被使用。

Just because you can write arbitrarily complex expressions doesn't mean you should.仅仅因为您可以编写任意复杂的表达式并不意味着您应该这样做。 You could write code like map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables)) but you don't .你可以写像map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))但你没有

I you think your code may be misleading use other form to express it.我你认为你的代码可能会误导使用其他形式来表达它。

It's like using extra brackets in expressions to avoid questions about operators precedence.这就像在表达式中使用额外的括号来避免有关运算符优先级的问题。 I'ts always a good investment to make your code readable.我并不总是让你的代码可读的好投资。

I prefer to use unpacking only for simple tasks like swap.我更喜欢仅对交换等简单任务使用解包。

The original idea of starred expression at the lhs is to improve the readability of iterable unpacking as below : lhs的starred表达式的初衷是为了提高iterable解包的可读性,如下:

first_param, rest_param, third_param = param[0], param[1:-1], param[-1]

This statment is equvailent to这个说法等同于

first_param, *rest_param, third_param = param

In the statement above, starred expression is used to 'catch' all elements that are not assigned to 'mandatory targets' ( first_param & third_param in this example)在上面的语句中,带星号的表达式用于“捕获”所有未分配给“强制目标”的元素(本例中为first_paramthird_param

The use of starred expression at the lhs has the following rules:在 lhs 处使用带星号的表达式有以下规则:

  1. at most one starred expression at the lhs, or the unpacking will not be unique在 lhs 最多一个加星的表情,否则解包不会是唯一的
*a,b,*c = range(5) # wrong
*a,b,c = range(5) # right
a,*b,c = range(5) # right
  1. In order to collect 'rest' elements, starred expression must be used with mandatory targets.为了收集“rest”元素,必须将带星号的表达式与强制目标一起使用。 Trailing comma is used to indicate mandatory targets do not exist尾随逗号用于表示强制目标不存在
*a = range(5) # wrong
*a, = range(5) # right

I belive if you master these two rules, you can deduce any result of starred expression at the lhs.我相信如果你掌握了这两个规则,你可以在 lhs 上推断出任何加星表达式的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM