web-dev-qa-db-ja.com

i = i + nは本当にi + = nと同じですか?

一方のコードブロックは機能しますが、もう一方は機能しません。 2番目のブロックが最初のブロックと同じであることを除けば、どちらを使用しても意味があります。それらは実質的に同じ操作です。

l = ['table']
i = []

バージョン1

for n in l:
    i += n
print(i)

出力:['t', 'a', 'b', 'l', 'e']

バージョン2

for n in l:
    i = i + n
print(i)

出力:

TypeError:listを( "str"ではなく)連結することしかできない


この奇妙なエラーの原因は何ですか?

55
Luke Bakare

彼らは同じである必要はありません。

+演算子を使用すると__add__メソッドが呼び出され、+=演算子を使用するとメソッド__iadd__が呼び出されます。これらのメソッドの1つが呼び出されたときに何が起こるかは、完全に問題のオブジェクト次第です。

x += yを使用するがx__iadd__メソッドを提供しない場合(またはメソッドがNotImplementedを返す場合)、__add__フォールバックとして使用され、x = x + yが発生します。

リストの場合、l += iterableを使用すると、実際にはリストliterableの要素で拡張されます。あなたの場合では、文字列からのすべての文字(これは反復可能です)はextend操作の間に追加されます。

デモ1:__iadd__を使う

>>> l = []
>>> l += 'table'
>>> l
['t', 'a', 'b', 'l', 'e']

デモ2:extendを使用しても同じことができます

>>> l = []
>>> l.extend('table')
>>> l
['t', 'a', 'b', 'l', 'e']

デモ3:リストと文字列を追加するとTypeErrorが発生します。

>>> l = []
>>> l = l + 'table'
[...]
TypeError: can only concatenate list (not "str") to list

+=のみを使用すると、ここでTypeErrorが得られます。これは、__iadd__のみが拡張動作を実装しているためです。

デモ4:よくある落とし穴:+=は新しいリストを作成しません。これを確認するには、is演算子を使用して等しいオブジェクトIDを確認します。

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l += [1, 2, 3] # uses __iadd__, mutates l in-place
>>> l is l_ref # confirm that l and l_ref are names for the same object
True
>>> l
[1, 2, 3]
>>> l_ref # mutations are seen across all names
[1, 2, 3]

ただし、l = l + iterable構文は新しいリストを作成します。

>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l = l + [1, 2, 3] # uses __add__, builds new list and reassigns name l
>>> l is l_ref # confirm that l and l_ref are names for different objects
False
>>> l
[1, 2, 3]
>>> l_ref
[]

+=は元のリストを変更するので、場合によっては、これによって微妙なバグが発生する可能性があります。
l = l + iterable新しいリストを作成し、名前を再割り当てしますl

ボーナス

ドキュメントでこれを見つけることに対するNed Batchelderの挑戦

79
timgeb

後者の場合、エラーを回避するためにnの周りにリストをラップします。

for n in l:
    i = i + [n]
print(i)

あなたが得る

['table']

それでそれらは異なった操作です。

3
Jake

いいえ

7.2.1。拡張代入文

x += 1のような拡張代入式は、x = x + 1のように書き換えることができますが、同等の効果を得ることはできません。拡張バージョンでは、xは一度だけ評価されます。また、可能であれば、実際の操作はインプレースで実行されます。つまり、新しいオブジェクトを作成してそれをターゲットに割り当てるのではなく、古いオブジェクトが変更されます。

3
msc