Почему мне явно нужно .copy() массив/список numpy?

Почему мне нужно .copy() массив numpy (или список, хотя у них нет .copy(), но это хороший минимальный пример), когда я хочу получить его независимую копию, в то время как мне не нужно делать это с переменная? В чем причина такого поведения?

Например. установка a = 1 и b = a, а затем установка b = 2 не меняет a, тогда как a=([0]), b = a и b[0]=1 также изменяет a.

Изменить:

Похоже, что это очень распространенная проблема/вопрос, который можно задать по-разному, поэтому я не смог его найти. Для быстрого "Мне просто нужно понять, что происходит, чтобы двигаться дальше" я думаю, что ответ (и ответы на другие такие же/похожие вопросы) достаточно хорош. Для лучшего понимания я думаю, что эта ссылка, предоставленная @chthonicdaemon в комментариях, кажется хорошим началом точка для меня.

длинный фон

Во многих ситуациях мне приходится создавать массив numpy с некоторыми начальными значениями, который затем заполняется другими значениями во время работы моей программы. т.е.

logger = np.zeros(5)
#various things happen
logger[0] = 1
#more stuff happens
logger[3] = 1

вы поняли идею.

Но часто желательно сохранить базовый массив, чтобы иметь возможность сравнивать с ним результаты, поэтому я просто сначала настраиваю базу, а потом делаю из нее копии. Поэтому я ожидаю, что я просто мог бы настроить это так:

base = np.zeros(5)
logger = base
logger[0] = 1

чтобы

In [1]:base
Out[1]:array([ 0.,  0.,  0.,  0.,  0.])
In [2]:logger
Out[2]:array([ 1.,  0.,  0.,  0.,  0.])

Однако с приведенным выше массивом они остаются «подключенными», так что я получаю

In [1]:base
Out[1]:array([ 1.,  0.,  0.,  0.,  0.])

Я могу исправить это, явно используя

logger = base.copy()

но мне интересно, почему я должен.


person JC_CL    schedule 03.05.2018    source источник
comment
целые числа, строки и кортежи, например, являются неизменяемыми, они не могут быть изменены после их создания, списки, наборы и массивы numpy являются изменяемыми, они могут быть изменены   -  person Chris_Rands    schedule 03.05.2018
comment
Это точно такая же проблема, только с добавлением функции. Разница в том, что вы делаете две разные вещи с этими переменными. Изменение значения переменной с помощью присваивания типа logger = foo совершенно отличается от изменения объекта с помощью чего-то вроде logger[0] = 1.   -  person Aran-Fey    schedule 03.05.2018
comment
Это лучшее из известных мне объяснений того, как работают имена Python.   -  person chthonicdaemon    schedule 03.05.2018
comment
Если вы когда-либо изучали C/C++, вы можете легко это понять, так как он просто реализован как указатель.   -  person Sraw    schedule 03.05.2018
comment
logger и base ссылаются на один и тот же объект.   -  person hpaulj    schedule 03.05.2018
comment
Кажется, я начинаю понимать, что происходит/почему. Довольно сбивает с толку тот факт, что = имеет разные значения, в зависимости от того, к чему я его применяю.   -  person JC_CL    schedule 03.05.2018
comment
Мне не нравится этот дурак. Он не устраняет разницу между foo = bar и foo[0] = bar. Этот вопрос спрашивает, почему две разные операции делают две разные вещи, а не почему присваивание не создает копию.   -  person Aran-Fey    schedule 03.05.2018
comment
Также см. stackoverflow.com/questions/2612802/ Это говорит о простом list, но это тот же принцип. А более короткую статью с милыми диаграммами, в которой обсуждается то же самое, что и в статье Неда Батчелдера, но под немного другим углом, см. В других языках есть переменные, в Python есть имена   -  person PM 2Ring    schedule 03.05.2018


Ответы (1)


Списки (и массивы numpy) являются изменяемыми объектами. str и int являются неизменяемыми. Неизменяемые объекты нельзя изменить после их создания, поэтому:

a = 1
b = a

Оба указывают на неизменный объект int, значение которого равно 1. Когда вы меняете b, вы присваиваете ему новое значение:

b = 3

Теперь b указывает на другой объект, а a указатель не изменился.

Теперь списки взаимозаменяемы. Это означает, что вы можете изменить эти объекты после их создания.

alist = list((1, 2, 3))
newlist = alist

alist == newlist
True

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

Управление одним объектом влияет на обе переменные, поскольку они ссылаются на один и тот же объект-контейнер.

Копия делает новую копию списка, включая копию ссылок. (иногда упоминается как глубокая копия). Это создает новый объект-контейнер, поэтому при изменении одного из списков другой не меняется.

person Chen A.    schedule 03.05.2018
comment
Теперь списки неизменяемы. Разве это не должно быть изменчивым? - person JC_CL; 03.05.2018
comment
Не имеет значения, является ли объект изменчивым или нет. Важно то, что вы делаете с этим объектом. Если вы не изменяете список, вам не нужно его копировать. Разница заключается в foo = x и foo[0] = x, то есть в изменении переменной и изменении объекта. - person Aran-Fey; 03.05.2018
comment
@JC_CL Спасибо, что указали на это, я исправил - person Chen A.; 03.05.2018
comment
@ Аран-Фей, твоё последнее предложение попало в цель. Я не мог бы сформулировать это лучше: изменение переменной вместо объекта. - person Chen A.; 03.05.2018