web-dev-qa-db-ja.com

Pythonで「i == x」と「i = i + x」が異なるのはいつですか?

+=は、i = i +の標準表記とは異なる効果を持つ可能性があると言われました。 i += 1i = i + 1と異なる場合はありますか?

202
MarJamRob

これはオブジェクトiに完全に依存します。

+=__iadd__メソッド (存在する場合-__add__が存在しない場合はフォールバック)を呼び出しますが、+は-を呼び出します __add__メソッド1 または __radd__メソッドがいくつかのケースで2

APIの観点から見ると、__iadd__は、変更可能なオブジェクトをインプレース(変更されたオブジェクトを返す)の変更に使用することになっていますが、__add__新しいインスタンスimmutableオブジェクトの場合、両方のメソッドは新しいインスタンスを返しますが、__iadd__は、新しいインスタンスを古いインスタンスと同じ名前で現在のネームスペースに配置します。これが理由です

i = 1
i += 1

iをインクリメントするようです。実際には、新しい整数を取得して「上に」iを割り当てます。古い整数への参照が1つ失われます。この場合、i += 1i = 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]

最初の例では、baが同じオブジェクトを参照するため、b+=を使用すると、実際にb(およびaはその変更も確認します-結局、同じリストを参照しています。ただし、2番目の場合、b = b + [1, 2, 3]を実行すると、bが参照しているリストを取得し、それを新しいリスト[1, 2, 3]と連結します。次に、連結リストを現在のネームスペースにbとして保存します。前の行がbであったかどうかは関係ありません。


1x + yで、x.__add__が実装されていない場合、またはx.__add__(y)NotImplementedを返す場合[andx and yは異なるタイプを持ち、x + yy.__radd__(x) の呼び出しを試みます。だから、あなたが持っている場合

foo_instance += bar_instance

Foo__add__または__iadd__を実装していない場合、ここでの結果は次と同じです。

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2foo_instance + bar_instanceで、bar_instance.__radd__は、foo_instance.__add__ifの前に試行されますbar_instanceの型は、型のサブクラスですof foo_instance(例issubclass(Bar, Foo))。この理由は、Barが何らかの意味でFooよりも「上位」オブジェクトであるため、BarFooの動作をオーバーライドするオプションを取得するためです。

306
mgilson

カバーの下では、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]

l2l1と同じオブジェクトであり、l1を変更したため、l2も変更しました。

しかし:

>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]

ここでは、l1;を変更していません。代わりに、新しいリストl1 + [3]を作成し、l1を元のリストを指すようにして、l2という名前にリバウンドします。

+=バージョンでは、l1も再バインドしていました。その場合は、既にバインドされている同じlistに再バインドしているため、通常は無視できます。部。)

66
abarnert

i += xi = 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]
5
Deqing