Разница между a - = b и a = a - b в Python
Я недавно подал заявление этой решение для усреднения всех 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?
3 ответа:
Примечание: использование операций на месте для массивов 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:]
ввиду иa[:-1]
ввиду (второй элемент массиваa
была изменена).
a[:-1]
теперь[1, 1]
и NumPy теперь должен вычесть свой второй элемент это 1 (не 2 больше!) из второго элементаa[1:]
. Это делаетa[1:]
a просмотр значений[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
) для записи в один из массивов.
внутренне, разница в том, что это:
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 создает представления на них, поэтому их использование прямой доступ к памяти "исходного" объекта.
документы говорят :
идея расширенного назначения в Python заключается в том, что это не так просто более простой способ написать общую практику хранения результат двоичной операции в ее левом операнде, но также и A способ для рассматриваемого левого операнда знать, что он должен работать `на себя', а не создавать модифицированные копии себя.
как правило большого пальца, расширенное вычитание (
x-=y
) составляетx.__isub__(y)
на наместо работы если возможно, когда нормальное вычитание (x = x-y
) составляетx=x.__sub__(y)
. На изменяемые объекты, как числа эквиваленте. Но для изменчивых, таких как массивы или списки, как в вашем примере, они могут быть очень разными вещами.