2013-04-09 3 views
6
def f(x): 
    x=x/5. 
    return x 
def g(x): 
    x/=5. 
    return x 

x_var = np.arange(5,dtype=np.double) 
f(x_var) 
print x_var 
g(x_var) 
print x_var 

Output: 
[ 0. 1. 2. 3. 4.] 
[ 0. 0.2 0.4 0.6 0.8] 

Это поведение немного странно для меня, я всегда думал, что x/= 5. был эквивалентен x = x/5. , Но, очевидно, функция g (x) не создает новую ссылку с/= операцией. Может ли кто-нибудь предложить объяснение этому?Неожиданное поведение при самоопределении numpy

ответ

5

Я всегда думал, что x/= 5. было эквивалентно х = х/5

Это, если класс не переопределяет оператор __idiv__, как numpy.ndarray делает. numpy.ndarray переопределяет его, чтобы изменить массив на месте, что хорошо, потому что он позволяет избежать необходимости создания новой копии массива, когда копирование не требуется. Как вы можете догадаться, он также переопределяет остальных операторов __i*__.

+0

Спасибо за объяснение, я не смог найти документацию, которая заставила бы меня ожидать такого поведения. – bluecat

+5

Это не проблема с numpy, это объекты-объекты, проходящие по ссылке. Реализация по умолчанию для всех '__i * __' заключается в том, чтобы выполнить операцию на месте, если это возможно, прочитайте [docs] (http://docs.python.org/reference/datamodel.html#object.__iadd__). У вас будет такая же проблема, передавая список Python в функцию, которая использовала 'def f (a): a * = 3; return a', он изменит исходный объект, с которым вы его вызвали, а не просто возвращает измененную копию. – Jaime

+1

Спасибо, это была ссылка на документацию, которую я искал. Поэтому, если это возможно, он должен выполнять операцию на месте. Если __i * __ недоступен, он по умолчанию выполнит обычную операцию __ * __. – bluecat

3

Операторы на месте Python позволяют операции изменить объект 0 слева, а не на новый, вместо. Вы увидите то же поведение с list с и другими встроенными изменяемыми типами:

x = [] 
y = x 
x += [1] 
print y # prints [1] because x still refers to the same object as y 
x = x + [2] 
print x # prints [1, 2] for obvious reasons 
print y # prints [1] again because x has been rebound to a new list 
      # while y still refers to the original one 

Так это ожидаемое поведение.

При работе с неизменяемыми типами, конечно же, создается новый объект, потому что существующее невозможно изменить.

0

Я всегда думал, что x/= 5. был эквивалентен x = x/5.

Это не так. Когда вы делаете x=x/5., вы на самом деле выполняя две отдельных операций под капотом:

temporary = x/5. 
x = temporary 

тогда, когда вы используете оператор /=, вы явно просите Python, чтобы изменить ваш объект x на месте , без создания временного объекта. Как упоминалось в других ответах, оператор / вызывает метод __div__ вашего объекта (если он есть), а /= вызывает свой метод __idiv__.

Модифицированная модификация весьма полезна, если объект, который вы изменяете, большой: не нужно создавать как-то большой объект. Тем не менее, это может вас укусить, если вы не будете осторожны. В частности, обычно нет такой идеи, чтобы функция могла изменять свои входные параметры без предупреждения ...