나는 +=표준 표기법과는 다른 효과를 가질 수 있다고 들었습니다 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하면 이전 정수에 대한 하나의 참조가 손실됩니다. 이 경우와 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]
on을 사용할 때 첫 번째 예제 에서 동일한 객체를 참조 b하고 a참조하면 실제로 변경됩니다 (그리고 그 변경 사항도 볼 수 있습니다-결국 동일한 목록을 참조합니다). 그러나 두 번째 경우, 내가 할 때 이것은 참조 하는 목록을 가져 와서 새로운 목록과 연결합니다 . 그 다음으로 현재 이름 공간에 연결된 목록을 저장 - 무엇에 대한 어떤 관련하여 이전에 선이었다.+=bbab = b + [1, 2, 3]b[1, 2, 3]bb
1 발현은 x + y, 경우는 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)
이 식에서는 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_concatC API 함수는 Python dunder 메소드보다 엄격한 요구 사항을 가지고 있지만 ... 대답과 관련이 있다고 생각하지 않습니다. 주요 차이점은 +=과 같이 행동하기 전에 전체 추가 를 시도한다는 것 +입니다.
표지 아래에서 i += 1다음과 같이하십시오.
try:
i = i.__iadd__(1)
except AttributeError:
i = i.__add__(1)
하지만이 i = i + 1같은 무언가를 :
i = i.__add__(1)
이것은 약간 지나치게 단순화되었지만 아이디어를 얻습니다. 파이썬은 유형 뿐만 아니라 메소드를 +=작성하여 특수하게 처리 할 수있는 __iadd__방법을 제공합니다 __add__.
의도는와 같은 가변 유형 list은 스스로 변이하고 __iadd__( self매우 까다로운 작업을 수행하지 않으면 return ),과 같은 불변 유형 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호출 하지만 결과를 리 바인드 할 수 없다는 것을 의미합니다 . 따라서 2를 반환하고로 설정된 상태로 둡니다 . 약간 혼란 스럽습니다. __add__AttributeErrori=1; operator.iadd(i, 1)i1
+=extend()목록의 경우 처럼 작동 합니다.