一方のコードブロックは機能しますが、もう一方は機能しません。 2番目のブロックが最初のブロックと同じであることを除けば、どちらを使用しても意味があります。それらは実質的に同じ操作です。
l = ['table']
i = []
for n in l:
i += n
print(i)
出力:['t', 'a', 'b', 'l', 'e']
for n in l:
i = i + n
print(i)
出力:
TypeError:listを( "str"ではなく)連結することしかできない
この奇妙なエラーの原因は何ですか?
彼らは同じである必要はありません。
+
演算子を使用すると__add__
メソッドが呼び出され、+=
演算子を使用するとメソッド__iadd__
が呼び出されます。これらのメソッドの1つが呼び出されたときに何が起こるかは、完全に問題のオブジェクト次第です。
x += y
を使用するがx
が__iadd__
メソッドを提供しない場合(またはメソッドがNotImplemented
を返す場合)、__add__
はフォールバックとして使用され、x = x + y
が発生します。
リストの場合、l += iterable
を使用すると、実際にはリストl
がiterable
の要素で拡張されます。あなたの場合では、文字列からのすべての文字(これは反復可能です)は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
。
ボーナス
後者の場合、エラーを回避するためにn
の周りにリストをラップします。
for n in l:
i = i + [n]
print(i)
あなたが得る
['table']
それでそれらは異なった操作です。
いいえ
x += 1
のような拡張代入式は、x = x + 1
のように書き換えることができますが、同等の効果を得ることはできません。拡張バージョンでは、xは一度だけ評価されます。また、可能であれば、実際の操作はインプレースで実行されます。つまり、新しいオブジェクトを作成してそれをターゲットに割り当てるのではなく、古いオブジェクトが変更されます。