Я хочу вызвать атрибут класса и добавить его в список. Вот простой скрипт:
class class_1():
def __init__(self):
self.x = np.array([0, 0])
def update(self):
self.x += 1
return self.x
cl_1 = class_1()
a = []
for i in range(3):
y = cl_1.update()
print(y)
a.append(y)
print(a)
# output:
[1 1]
[2 2]
[3 3]
[array([3, 3]), array([3, 3]), array([3, 3])]
но я ожидаю [array([1, 1]), array([2, 2]), array([3, 3])]
как окончательное значение списка a
. Я проверил, что нет проблем с номерами python:
class class_2():
def __init__(self):
self.x = 0
def update(self):
self.x += 1
return self.x
cl_2 = class_2()
a = []
for i in range(3):
y = cl_2.update()
print(y)
a.append(y)
print(a)
#output
1
2
3
[1, 2, 3]
В отличие от целых чисел Python, массивы numpy действительно допускают модификацию на месте. С целым числом Python, которое является неизменным, x += 1
и x = x + 1
имеют одинаковый результат. В обоих случаях x
восстанавливается до нового целочисленного объекта.
Когда вы делаете self.x += 1
в массив numpy, вы не изменяете ссылку, на self.x
указывает self.x
Новый массив не выделяется, но вместо этого каждый элемент во внутреннем буфере существующего массива увеличивается. Обратите внимание, что назначение все еще происходит, но с той же ссылкой, что и раньше.
Чтобы имитировать поведение целого в этом контексте, явно выпишите нужную операцию:
self.x = self.x + 1
В этом случае self.x + 1
является полностью новым массивом, который затем может быть переназначен на self.x
в качестве целого.
У вас есть два варианта, когда вы хотите исправить свой код:
Если все в порядке с созданием нового массива каждый раз и отбрасыванием предыдущего массива, измените метод update
как показано выше. В некотором смысле это решение является самым чистым, поскольку оно минимизирует влияние на внешние ссылки. В то же время вы можете не создавать новую копию каждый раз.
Если вы хотите избежать создания ненужных копий в большинстве случаев (или, по крайней мере, иметь контроль над созданием копий), используйте предложение @DYZ. Вместо a.append(y)
, do
a.append(y.copy())
или, альтернативно, сделать копию немедленно:
y = cl_2.update().copy()
TL; DR
Здесь происходит одна из самых распространенных ловушек начинающих: создание списка из ссылок на один и тот же изменяемый объект, а затем мутация объекта. Все ссылки заканчиваются последним значением объекта, как и ожидалось.