나는 +=
표준 표기법과는 다른 효과를 가질 수 있다고 들었습니다 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
참조하면 실제로 변경됩니다 (그리고 그 변경 사항도 볼 수 있습니다-결국 동일한 목록을 참조합니다). 그러나 두 번째 경우, 내가 할 때 이것은 참조 하는 목록을 가져 와서 새로운 목록과 연결합니다 . 그 다음으로 현재 이름 공간에 연결된 목록을 저장 - 무엇에 대한 어떤 관련하여 이전에 선이었다.+=
b
b
a
b = b + [1, 2, 3]
b
[1, 2, 3]
b
b
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_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)
이것은 약간 지나치게 단순화되었지만 아이디어를 얻습니다. 파이썬은 유형 뿐만 아니라 메소드를 +=
작성하여 특수하게 처리 할 수있는 __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__
AttributeError
i=1; operator.iadd(i, 1)
i
1
+=
extend()
목록의 경우 처럼 작동 합니다.