2017-02-21 2 views
5

Я начинаю и путаю, когда изучаю python. Если у меня есть следующий код Python:Python confusion - условное обозначение, имя и значение

import numpy as np 
X = np.array([1,0,0]) 
Y = X 
X[0] = 2 
print Y 

Y будет показано, что array([2, 0, 0])

Однако, если я делаю следующее:

import numpy as np 
X = np.array([1,0,0]) 
Y = X 
X = 2*X 
print Y 

Y еще array([1,0,0])

Что продолжается?

+1

Здесь вы изменяете * локальную переменную 'X' *, это изменение не будет отражено в' Y', так как 'Y' даже не знает' X' существует. –

+0

Спасибо, извините, я не был ясен на этом посту. Я изменяю строку X [0] = 2 на X = 2 * X в исходном коде выше. – SunnyIsaLearner

+1

Поскольку назначение операции среза/индексации * мутирует структуру данных *, где, используя оператор '*', генерирует * новую * структуру данных. Также прочитайте и поймите это: http://nedbatchelder.com/text/names.html –

ответ

5

это потому, что X и Y являются ссылками на тот же объект np.array([1,0,0]) это означает, что независимо от того, является ли вызов делается черезX или Y, результат будет тем же самым, но изменение ссылка одного, не имеет никакого эффекта.

Если вы пишете:

X = np.array([1,0,0]) 
Y = X 

в основном то, что происходит в том, что есть две локальные переменныеX и Y, что относятся к тому же объекту. Таким образом, память выглядит следующим образом:

 +--------+ 
Y -> |np.array| <- X 
    +--------+ 
    |[1,0,0] | 
    +--------+ 

Теперь, если вы X[0] = 2, которая в основном короткие для:

X.__setitem__(0,2) 

так вы вызываете метод объекта. Так что теперь память выглядит следующим образом:

 +--------+ 
Y -> |np.array| <- X 
    +--------+ 
    |[2,0,0] | 
    +--------+ 

Однако, если вы пишете:

X = 2*X 

первый 2*X является оценивали.Теперь 2*X короток для:

X.__rmul__(2) 

(Python первый выглядит, если 2 поддерживает __mul__ для X, но так как 2 поднимет NotImplementedException), Python будет Откат к X.__rmul__). Теперь X.__rmul__не изменяется X: он оставляет X неповрежденным, но создает новый массив и возвращает его. X ловит этот новый массив, который теперь ссылается на этот массив).

, который создает новый объект array: array([4, 0, 0]) и затем Xссылки на этот новый объект. Так что теперь память выглядит следующим образом:

 +--------+   +--------+ 
Y -> |np.array|  X ->|np.array| 
    +--------+   +--------+ 
    |[2,0,0] |   |[4,0,0] | 
    +--------+   +--------+ 

Но как вы можете видеть, Y все еще ссылки на старый объект.

+0

'__setitem__' отсутствует некоторые символы подчеркивания –

+0

@MadPhysicist: спасибо, что заметили это. –

+0

Кроме того, '__mul__' - это метод, подобный' __setitem__', поэтому вы на самом деле не объясняете, почему умножение не распространяется на 'Y'. –

8

подумайте об этом так: знак равенства в python присваивает ссылки.

Y = X делает Y указывают на тот же адрес X указывает на

X[0] = 2 делает х [0] указывают на 2

X = 2*X делает X точку на новую вещь, но Y все еще указывает на адрес оригинал X, поэтому Y не изменяется

это не совсем верно, но достаточно близко, чтобы понять принцип

+0

Учитывая, как работают объекты 'np.ndarray',« X [0] = 2 делает x [0] указывает на 2 ", это просто неправильно, но не будет уменьшать из-за этого последнего отказа. –

2

Когда вы говорите X = np.array([1, 0, 0]), вы создаете объект, который имеет некоторые методы и некоторые внутренние буферы, которые содержат фактические данные и другую информацию в нем.

Выполнение Y = X наборов Y, чтобы ссылаться на этот же фактический объект. Это называется привязкой к имени в Python. Вы связали тот же объект, который был связан с X с именем Y.

Выполнение X[0] = 2 вызывает метод __setitem__ объекта, который выполняет некоторые операции с базовыми буферами. Если изменяется объект на месте. Теперь, когда вы печатаете значения X или Y, цифры, которые выходят из буфера этого объекта, - 2, 0, 0.

Выполнение X = 2 * X переводит на X.__rmul__(2). Этот метод не изменяет X на месте. Он создает и возвращает новый объект массива, каждый из элементов которого в два раза соответствует соответствующему элементу X. Затем вы привязываете новый объект к имени X. Однако имя Y все еще привязано к исходному массиву, потому что вы ничего не сделали для его изменения. В стороне, X.__rmul__ используется, потому что 2.__mul__(X) не работает. Массивные массивы естественно определяют умножение, которое должно быть коммутативным, поэтому X.__mul__ и X.__rmul__ должны к тому же.

Интересно отметить, что вы также можете сделать X *= 2, который будет распространять изменения на Y. Это связано с тем, что оператор *= переводит на метод __imul__, который делает.

+0

Спасибо за указание X * = 2. Я должен помнить об этом! – SunnyIsaLearner

3

Это скорее соглашение и имена, чем ссылка и значение.

При назначении:

Y = X 

Тогда имя Y относится к объекту, который именем X указывает.В некотором смысле указатель X и Y указывают на тот же объект:

X is Y # True 

В is проверяет имена указывают на тот же объект!


Тогда это становится сложным: вы выполняете некоторые операции над массивами.

X[0] = 2 

Это называется «присвоение пункт» и называет

X.__setitem__(0, 2) 

Что __setitem__ следует делать (конвенции), чтобы обновить некоторые значения в контейнере X. Таким образом, X должен по-прежнему указывать на тот же объект.

Однако X * 2 является «умножением», и в соглашении говорится, что это должно создать новый объект (опять же соглашение, вы можете изменить это поведение, перезаписав X.__mul__). Так что, когда вы делаете

X = X * 2 

от имени X теперь ссылается на новый объект, который X * 2 создано:

X is Y # False 

Обычно общие библиотеки следуют эти соглашения, но важно подчеркнуть, что вы можете укомплектовать изменить это!

+1

Я бы сказал, что это скорее вопрос реализации, чем конвенция. Хотя соглашение может диктовать одно или другое, реализация массивов numpy - это то, что определяет поведение операторов в конце. В этом случае, конечно, реализация следует за конвенцией. –

+0

Кроме того, технически ОП спросил о '2 * X', поэтому' __rmul__', но опять же, для numpy это коммутативно. –

+0

Я изменил название вопроса на основе вашего предложения. Трудно даже задавать вопросы для новичка. Благодаря! – SunnyIsaLearner

Смежные вопросы