空のリスト([] =“”など)に割り当てないとエラーにならないのはなぜですか?


110

Python 3.4では、私は入力しています

[] = "" 

正常に動作し、例外は発生しません。もちろんそれ以降[]は同じではありません""

[] = ()

また、正常に動作します。

"" = []

期待通りに例外が発生しますが、

() = ""

期待通り例外が発生します。どうしたの?

回答:


132

あなたは平等について比較していません。あなたはされて割り当てます

Pythonでは、複数のターゲットに割り当てることができます。

foo, bar = 1, 2

2つの値をそれぞれfooとに割り当てbarます。必要なのは、右側にシーケンスまたは反復可能で、左側に名前のリストまたはタプルです。

あなたがするとき:

[] = ""

名前の空のリストに空のシーケンス(空の文字列はまだシーケンスです)を割り当てました。

基本的には次のことと同じです。

[foo, bar, baz] = "abc"

最終的にはfoo = "a"bar = "b"およびbaz = "c"になりますが、文字は少なくなります。

ただし、文字列に割り当てることはできないため、割り当て""の左側では機能せず、常に構文エラーになります。

割り当てステートメントのドキュメントを参照してください。

割り当てステートメントは式リストを評価し(これは単一の式またはコンマ区切りのリストであり、後者はタプルを生成することに注意してください)、単一の結果オブジェクトを各ターゲットリストに左から右に割り当てます。

そして

ターゲットリストへのオブジェクトの割り当ては、オプションでかっこまたは角かっこで囲まれ、次のように再帰的に定義されます。

強調鉱山

空のリストに対してPythonが構文エラーをスローしないことは、実際には少しバグです!公式に文書化された文法では、空のターゲットリストは許可されておらず、空の()場合はエラーが発生します。バグ23275を参照してください。無害なバグと見なされます。

出発点は、これが非常に長い間存在し、無害であることを認識することです。

空のリストに割り当てても空のタプルには割り当てないのなぜですか?も参照してください


36

ドキュメントの割り当てステートメントのセクションルールに従います。

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

target listがターゲットのコンマ区切りリストの場合:オブジェクトは、ターゲットリスト内のターゲットと同じ数のアイテムを持つイテラブルでなければならず、アイテムは左から右に対応するターゲットに割り当てられます。

オブジェクトは、ターゲットリストのターゲットと同じ数のアイテムを含むシーケンスである必要があり、アイテムは対応するターゲットに左から右に割り当てられます。

だから、あなたが言うとき

[] = ""

"" は反復可能であり(有効なPython文字列はすべて反復可能です)、リストの要素に展開されます。

例えば、

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

空の文字列と空のリストがあるため、アンパックするものはありません。したがって、エラーは発生しません。

しかし、これを試してください

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

その[] = "1"場合、あなたは文字列を開梱しようとしています"1"空の変数リストにます。そのため、「アンパックするには値が多すぎます(予期される0)」という文句を言います。

同じように [a] = ""何が本当に解凍しないようにしてケース、あなたは、空の文字列を持っていますが、あなたは、再び、可能ではない一つの変数、の上に開梱されています。そのため、「アンパックするには0を超える値が必要です」というメッセージが表示されます。

それとは別に、気づいたように、

>>> [] = ()

()空のタプルなので、エラーもスローしません。

>>> ()
()
>>> type(())
<class 'tuple'>

空のリストでアンパックすると、アンパックするものはありません。エラーはありません。


しかし、あなたがするとき

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

エラーメッセージが示すように、文字列リテラルに割り当てようとしています。それは不可能です。そのため、エラーが発生します。それは言うようなものです

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

内部

内部的には、この割り当て操作はUNPACK_SEQUENCEopコードに変換され、

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

ここでは、文字列が空なので、時間をUNPACK_SEQUENCE解凍し0ます。しかし、あなたがこのようなものを持っているとき

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

シーケンス123は、右から左にスタックにアンパックされます。したがって、スタックの一番上が、1次が2、最後がになります3。次に、スタックの一番上から、左側の式の変数に1つずつ割り当てます。


ところで、Pythonでは、これにより、同じ式で複数の割り当てを行うことができます。例えば、

a, b, c, d, e, f = u, v, w, x, y, z

これが機能するのは、右側の値を使用してタプルが作成され、左側の値の上に展開されるためです。

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

しかし、古典的なスワッピングテクニックでa, b = b, aは、スタックの一番上の要素の回転を使用します。あなたが唯一の二、三の要素を持っている場合、それらは特別で処理されているROT_TWOし、ROT_THREE代わりにタプルを構築し、開梱の指示。

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

dis('[] = ""')を呼び出さずに使用することもできますcompile()
Andrea Corbellini、2015年

最後の例のメソッドを使用して、4つ以上の変数/要素をスワップするとどうなるか説明できますか?
nanofarad

@hexafractionこれは、右側にすべての要素を含む新しいタプルを構築し、左側の変数にそれらをアンパックします。
thefourtheye

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.