web-dev-qa-db-ja.com

展開、拡張展開、ネストされた拡張展開

これらの表現を検討してください...我慢してください...これは長いリストです...

(注:一部の表現は繰り返されます-これは単に「コンテキスト」を提示するためです)

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

このような複雑さと混乱をどのように理解しますか。そのような式の結果を手動で計算するときに、常に正しいことができる方法。または、他の誰かのコードを読むとき、それらを無視して、式が実際に何をしているかを推測しようとしないでください。

91
treecoder

この記事の長さについては謝罪しますが、完全性を選択することにしました。

いくつかの基本的なルールを知ったら、それらを一般化するのは難しくありません。いくつか例を挙げて説明するように最善を尽くします。これらを「手作業で」評価することについて話しているので、いくつかの簡単な置換ルールを提案します。基本的に、すべての反復可能要素が同じ方法でフォーマットされていると、式を理解しやすくなる場合があります。

解凍のみを目的とする場合、以下の置換が=の右側で有効です(つまり、rvaluesの場合):

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

値がアンパックされない場合は、置換を元に戻します。 (詳細については、以下を参照してください。)

また、「裸の」コンマが表示されたら、トップレベルのタプルがあるふりをしてください。左側と右側の両方でこれを行います(つまりlvaluesおよびrvalues):

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

これらの単純なルールを念頭に置いて、いくつかの例を示します。

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

上記のルールを適用して、"XY"('X', 'Y')に変換し、かっこで囲まれたコンマをカバーします。

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

ここでの視覚的な対応により、割り当てがどのように機能するかが明らかになります。

間違った例を次に示します。

(a,b), c = "XYZ"

上記の置換ルールに従って、以下を取得します。

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

これは明らかに誤りです。ネストされた構造は一致しません。次に、少し複雑な例でどのように機能するかを見てみましょう。

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

上記のルールを適用すると、

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

しかし、'this'は展開されず、cに直接割り当てられることが構造から明らかです。したがって、置換を取り消します。

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

では、タプルでcをラップするとどうなるか見てみましょう。

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

になる

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

繰り返しますが、エラーは明らかです。 cはネイキッド変数ではなく、シーケンス内の変数であるため、右側の対応するシーケンスは(c,)に展開されます。ただし、シーケンスの長さは異なるため、エラーがあります。

*演算子を使用した拡張アンパック用。これはもう少し複雑ですが、それでもかなり簡単です。 *が前に付いた変数はリストになり、変数名に割り当てられていない対応するシーケンスの項目が含まれます。かなり単純な例から始めます。

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

これは

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

これを分析する最も簡単な方法は、最後から作業することです。 'X'aに割り当てられ、'Y'cに割り当てられます。シーケンスの残りの値はリストに入れられ、bに割り当てられます。

(*a, b)(a, *b)などの左辺値は、上記の特殊なケースにすぎません。 1つの左辺値シーケンス内に2つの*演算子を含めることはできません。曖昧になるためです。このような(a, *b, *c, d)-bまたはcのような値はどこにありますか?ネストされたケースについてはすぐに検討します。

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

ここで、エラーは一目瞭然です。ターゲット(*a)はタプル内になければなりません。

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

裸のコンマがあるため、これは機能します。ルールを適用しています...

(*a,) = (1, 2)

*a以外の変数はないため、*aは右辺値シーケンスのすべての値を丸みします。 (1, 2)を単一の値に置き換えるとどうなりますか?

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

になる

(*a,) = 1

繰り返しますが、ここでのエラーは一目瞭然です。シーケンスではないものをアンパックすることはできません。*aはアンパックするために何かを必要とします。シーケンスに入れます

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

これは同等です

(*a,) = (1,)

最後に、これは混同の共通点です。(1)1と同じです。タプルと算術ステートメントを区別するにはコンマが必要です。

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

ネスティング用になりました。実際、この例は「ネスト」セクションにはありません。おそらく、それがネストされていることに気づかなかったのでしょうか?

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

になる

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

最上位のタプルの最初の値が割り当てられ、最上位のタプルの残りの値(2および3)が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

最初の行でエラーがスローされる理由については既に説明しました。 2行目は馬鹿げていますが、これが機能する理由は次のとおりです。

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

前に説明したように、私たちは端から働きます。 3cに割り当てられ、残りの値は*が前に付いた変数に割り当てられます。この場合は(a, b)です。したがって、これは(a, b) = (1, 2)と同等です。これは、正しい数の要素があるために動作します。これが実際のコードに表示される理由は考えられません。同様に、

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

になる

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

端から作業すると、's'cに割り当てられ、('t', 'h', 'i')(a, *b)に割り当てられます。再び最後から作業すると、't'aに割り当てられ、('h', 'i')がリストとしてbに割り当てられます。これは、動作中のコードには決して現れない別の愚かな例です。

101
senderle

Python 2タプルのアンパックは非常に簡単です。左側の各名前は、シーケンス全体または右側のシーケンスの単一項目に対応します。名前が任意のシーケンスの単一項目に対応する場合、すべてのアイテムをカバーするのに十分な名前が必要です。

ただし、拡張アンパックは非常に強力であるため、混乱を招く可能性があります。実際には、最後に10個以上の有効な例を実行しないでください。データが構造化されている場合は、リストのような非構造化フォームではなく、dictまたはクラスインスタンスにする必要があります。

明らかに、新しい構文は悪用される可能性があります。あなたの質問への答えは、あなたはそうではないそのような表現を読む必要はないということです-彼らは悪い習慣であり、私は彼らが使用されることを疑います。

複雑な式を任意に記述できるからといって、そうすべきだというわけではありません。 map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))のようなコードを書くこともできますが、do n'tです。

7
agf

あなたのコードは誤解を招く可能性があると思いますが、他の形式を使用してそれを表現します。

演算子の優先順位に関する質問を避けるために、式で余分な括弧を使用するようなものです。コードを読みやすくすることは、常に良い投資ではありません。

スワップなどの単純なタスクにのみアンパックを使用することを好みます。

3
Michał Šrajer