+=
の標準表記とは異なる効果を持つ可能性があると言われましたi = i +
。i += 1
と違うケースはありi = i + 1
ますか?
i=[1,2,3];i=i+[4,5,6];i==[1,2,3,4,5,6]
ですTrue
。多くの開発者はid(i)
、一方の操作の変更に気づかないかもしれませんが、もう一方の変更には気づきません。
+=
の標準表記とは異なる効果を持つ可能性があると言われましたi = i +
。i += 1
と違うケースはありi = i + 1
ますか?
i=[1,2,3];i=i+[4,5,6];i==[1,2,3,4,5,6]
ですTrue
。多くの開発者はid(i)
、一方の操作の変更に気づかないかもしれませんが、もう一方の変更には気づきません。
回答:
これは完全にオブジェクトに依存しますi
。
+=
__iadd__
メソッドを呼び出します(存在する場合- __add__
存在しない場合はフォールバックします)一方+
で、__add__
メソッド1または__radd__
いくつかのケースではメソッド2を呼び出します。
APIの観点から、__iadd__
変更可能なオブジェクトを変更するために使用されることになっている場所で、一方、(変異したオブジェクトを返す)__add__
返す必要があり、新しいインスタンス何かのを。不変オブジェクト、両方の方法は、新しいインスタンスを返しますが、__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
参照しているため、私が+=
on を使用するb
と実際に変更されますb
(そしてa
、その変更もわかります-結局、同じリストを参照しています)。ただし、2番目のケースではb = b + [1, 2, 3]
、これを行うと、b
参照しているリストを取得して、新しいリストに連結します[1, 2, 3]
。その後、現在のネームスペースで連結されたリストを保存するb
-何のためノーに関してはb
前のラインでした。
1式においてはx + y
、IFがx.__add__
実装されていない場合、またはx.__add__(y)
リターンNotImplemented
と x
とは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__
た場合のタイプbar_instance
のタイプのサブクラスであるfoo_instance
(例えば、issubclass(Bar, Foo)
)。これが合理的であるのBar
は、ある意味で「高レベル」オブジェクトは、Foo
その動作Bar
をオーバーライドするオプションを取得する必要があるためFoo
です。
+=
呼び出し__iadd__
、それ以外の場合は追加と再バインドにフォールバックします。そのためi = 1; i += 1
、がない場合でも機能しますint.__iadd__
。しかし、そのマイナーニット以外、素晴らしい説明。
int.__iadd__
ばかりだといつも思っていました__add__
。今日は何か新しいことを学んでよかったです:)。
x + y
通話y.__radd__(x)
の場合は、x.__add__
存在して(または戻っていないNotImplemented
とx
し、y
さまざまなタイプがある)
nb_inplace_add
またはsq_inplace_concat
、およびそれらのC API関数には、Python dunderメソッドよりも厳しい要件があります。しかし、それは答えに関連するとは思いません。主な違いは、すでに説明+=
したように+
、のように動作するようにフォールバックする前に、インプレース追加を行おうとすることです。
カバーの下で、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
はすでにバインドされているものに再バインドしているだけなので、通常はその部分を無視できます。)
__iadd__
実際に呼び出す__add__
のイベントにAttributeError
?
i.__iadd__
呼び出さないでください__add__
。それi += 1
が呼び出すこと__add__
です。
i = i.__iadd__(1)
- オブジェクトを適切に変更iadd
できますが、そうする必要はなく、どちらの場合でも結果を返すことが期待されています。
operator.iadd
呼び出すことを意味します__add__
がAttributeError
、結果を再バインドすることはできません。そのため、i=1; operator.iadd(i, 1)
2を返し、にi
設定したままにし1
ます。これは少し混乱します。
+=
extend()
リストの場合と同様に機能します。