+=
は、i = i +
の標準表記とは異なる効果を持つ可能性があると言われました。 i += 1
がi = i + 1
と異なる場合はありますか?
これはオブジェクトi
に完全に依存します。
+=
は __iadd__
メソッド (存在する場合-__add__
が存在しない場合はフォールバック)を呼び出しますが、+
は-を呼び出します __add__
メソッド1 または __radd__
メソッドがいくつかのケースで2。
APIの観点から見ると、__iadd__
は、変更可能なオブジェクトをインプレース(変更されたオブジェクトを返す)の変更に使用することになっていますが、__add__
は新しいインスタンス。 immutableオブジェクトの場合、両方のメソッドは新しいインスタンスを返しますが、__iadd__
は、新しいインスタンスを古いインスタンスと同じ名前で現在のネームスペースに配置します。これが理由です
i = 1
i += 1
i
をインクリメントするようです。実際には、新しい整数を取得して「上に」i
を割り当てます。古い整数への参照が1つ失われます。この場合、i += 1
はi = i + 1
とまったく同じです。しかし、ほとんどの可変オブジェクトでは、それは別の話です。
具体例として:
a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a #[1, 2, 3, 1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
に比べ:
a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
最初の例では、b
とa
が同じオブジェクトを参照するため、b
で+=
を使用すると、実際にb
(およびa
はその変更も確認します-結局、同じリストを参照しています。ただし、2番目の場合、b = b + [1, 2, 3]
を実行すると、b
が参照しているリストを取得し、それを新しいリスト[1, 2, 3]
と連結します。次に、連結リストを現在のネームスペースにb
として保存します。前の行がb
であったかどうかは関係ありません。
1式x + y
で、x.__add__
が実装されていない場合、またはx.__add__(y)
がNotImplemented
を返す場合[andx
and y
は異なるタイプを持ち、x + y
は y.__radd__(x)
の呼び出しを試みます。だから、あなたが持っている場合
foo_instance += bar_instance
Foo
が__add__
または__iadd__
を実装していない場合、ここでの結果は次と同じです。
foo_instance = bar_instance.__radd__(bar_instance, foo_instance)
2式foo_instance + bar_instance
で、bar_instance.__radd__
は、foo_instance.__add__
ifの前に試行されますbar_instance
の型は、型のサブクラスですof foo_instance
(例issubclass(Bar, Foo)
)。この理由は、Bar
が何らかの意味でFoo
よりも「上位」オブジェクトであるため、Bar
はFoo
の動作をオーバーライドするオプションを取得するためです。
カバーの下では、i += 1
は次のようなことを行います。
try:
i = i.__iadd__(1)
except AttributeError:
i = i.__add__(1)
i = i + 1
は次のようなことを行います:
i = i.__add__(1)
これは少し単純化しすぎていますが、あなたはアイデアを得ます:Pythonは、+=
メソッドと__iadd__
メソッドを作成することで、__add__
を特別に処理する方法を型に与えます。
意図は、list
などの可変型は__iadd__
で自身を突然変異させ(そして、非常にトリッキーなことをしていない限り、self
を返します)、int
、実装しません。
例えば:
>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]
l2
はl1
と同じオブジェクトであり、l1
を変更したため、l2
も変更しました。
しかし:
>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]
ここでは、l1
;を変更していません。代わりに、新しいリストl1 + [3]
を作成し、l1
を元のリストを指すようにして、l2
という名前にリバウンドします。
(+=
バージョンでは、l1
も再バインドしていました。その場合は、既にバインドされている同じlist
に再バインドしているため、通常は無視できます。部。)
i += x
とi = i + x
を直接比較する例を次に示します。
def foo(x):
x = x + [42]
def bar(x):
x += [42]
c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]