파이썬에서 a-= b와 a = a-b의 차이점


90

나는 최근 에 행렬의 N 행을 평균화하기 위해이 솔루션을 적용 했습니다 . 솔루션은 일반적으로 작동하지만 7x1 어레이에 적용하면 문제가 발생했습니다. -=운영자를 사용할 때 문제가 있음을 알았습니다 . 작은 예를 들어 보려면 :

import numpy as np

a = np.array([1,2,3])
b = np.copy(a)

a[1:] -= a[:-1]
b[1:] = b[1:] - b[:-1]

print a
print b

다음을 출력합니다.

[1 1 2]
[1 1 1]

따라서, 어레이의 경우 a -= b와는 다른 결과를 생성한다 a = a - b. 지금까지이 두 가지 방법이 똑같다고 생각했습니다. 차이점은 무엇입니까?

매트릭스의 모든 N 행을 합산하는 방법이 7x4 매트릭스에서는 작동하지만 7x1 배열에서는 작동하지 않는 이유는 무엇입니까?

답변:


80

참고 : 메모리를 공유하는 NumPy 배열에서 내부 작업을 사용하는 것은 버전 1.13.0 이상에서 더 이상 문제가되지 않습니다 (자세한 내용은 여기 참조 ). 두 작업은 동일한 결과를 생성합니다. 이 답변은 이전 버전의 NumPy에만 적용됩니다.


계산에 사용되는 동안 배열을 변형하면 예기치 않은 결과가 발생할 수 있습니다!

질문의 예에서 뺄셈은 -=의 두 번째 요소 a수정 한 다음 의 세 번째 요소에 대한 연산에서 수정 된 두 번째 요소 를 즉시 사용합니다 a.

a[1:] -= a[:-1]단계별 로 수행되는 작업 은 다음과 같습니다 .

  • a데이터가있는 배열입니다 [1, 2, 3].

  • 우리는이 데이터 상에 두 개의보기가 있습니다 a[1:]입니다 [2, 3],하고 a[:-1]있다 [1, 2].

  • 제자리 빼기 -=가 시작됩니다. 의 첫 번째 요소 인 a[:-1]1이의 첫 번째 요소에서 뺍니다 a[1:]. 이것은로 수정 a되었습니다 [1, 1, 3]. 지금 우리가 가지고있는 a[1:]데이터의 도면 [1, 3], 및 a[:-1]데이터를 나타내는 도면이다 [1, 1](어레이의 두 번째 요소는 a변경되었다).

  • a[:-1]이제 [1, 1]NumPy는의 두 번째 요소에서 1 (더 이상 2가 아님) 인 두 번째 요소 빼야합니다 a[1:]. 이렇게하면 a[1:]값을 볼 수 있습니다 [1, 2].

  • a이제 값이있는 배열입니다 [1, 1, 2].

b[1:] = b[1:] - b[:-1]때문에이 문제가없는 b[1:] - b[:-1]만들고 새로운 제 배열하고이 배열에 값을 할당한다 b[1:]. 그것은 수정하지 않는 b견해 있도록 뺄셈 동안 자체 b[1:]b[:-1]변경하지 마십시오.


일반적인 조언은 겹치는 경우 한 뷰를 다른 뷰와 제자리에서 수정하지 않는 것입니다. 여기에는 연산자 -=, *=등 이 포함 out되며 범용 함수 ( np.subtract및 등 np.multiply) 에서 매개 변수를 사용 하여 배열 중 하나에 다시 씁니다.


4
나는 현재 받아 들여지는 것 보다이 대답을 더 선호합니다. 매우 명확한 언어를 사용하여 변경 가능한 객체를 제자리에서 수정 한 효과를 보여줍니다. 더 중요한 것은 마지막 단락에서 중첩 뷰에 대한 내부 수정의 중요성을 직접 강조하며, 이는이 질문에서 집으로 가져갈 교훈이되어야합니다.
Reti43

43

내부적으로 차이점은 다음과 같습니다.

a[1:] -= a[:-1]

다음과 같습니다.

a[1:] = a[1:].__isub__(a[:-1])
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))

이 동안 :

b[1:] = b[1:] - b[:-1]

이것에 매핑 :

b[1:] = b[1:].__sub__(b[:-1])
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))

어떤 경우에, __sub__()그리고 __isub__()비슷한 방식으로 작동합니다. 그러나 변경 가능한 객체는를 사용할 때 변경하고 자신 __isub__()을 반환해야하며 __sub__().

numpy 객체에 슬라이스 작업을 적용하면 뷰가 생성되므로이를 사용하면 "원본"객체의 메모리에 직접 액세스 할 수 있습니다.


11

문서는 말한다 :

파이썬에서 증강 할당 뒤에있는 아이디어는 이진 연산의 결과를 왼쪽 피연산자에 저장하는 일반적인 관행을 작성하는 것이 더 쉬운 방법 일뿐만 아니라 문제의 왼쪽 피연산자가 다음을 수행하는 방법이라는 것입니다. 자신의 수정 된 사본을 생성하는 대신 '자체적으로'작동해야한다는 것을 알고 있어야합니다.

엄지 손가락 법칙으로, 증강 빼기 ( x-=y)이다 x.__isub__(y)들면 IN 의 자리 표시 동작 IF 정상 빼기가 (가능 x = x-y)이다 x=x.__sub__(y). 정수와 같은 변경 불가능한 객체에서는 동일합니다. 그러나 예에서와 같이 배열 또는 목록과 같은 변경 가능한 항목의 경우 매우 다를 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.