+=の標準表記とは異なる効果を持つ可能性があると言われました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()リストの場合と同様に機能します。