Атрибут класса добавления Python с типом массива numpy

1

Я хочу вызвать атрибут класса и добавить его в список. Вот простой скрипт:

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]     
  • 0
    Это дубликат, который просили миллион раз (без размышлений о вас, ОП), но я не могу найти хорошего кандидата на молоток.
  • 1
    Обратите внимание, что вы всегда добавляете ссылку на один и тот же массив. Смысл + = в том, что он модифицируется на месте, а не создает копию.
Показать ещё 7 комментариев
Теги:
list
python-3.x

1 ответ

1
Лучший ответ

В отличие от целых чисел 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 в качестве целого.

У вас есть два варианта, когда вы хотите исправить свой код:

  1. Если все в порядке с созданием нового массива каждый раз и отбрасыванием предыдущего массива, измените метод update как показано выше. В некотором смысле это решение является самым чистым, поскольку оно минимизирует влияние на внешние ссылки. В то же время вы можете не создавать новую копию каждый раз.

  2. Если вы хотите избежать создания ненужных копий в большинстве случаев (или, по крайней мере, иметь контроль над созданием копий), используйте предложение @DYZ. Вместо a.append(y), do

    a.append(y.copy())
    

    или, альтернативно, сделать копию немедленно:

    y = cl_2.update().copy()
    

TL; DR

Здесь происходит одна из самых распространенных ловушек начинающих: создание списка из ссылок на один и тот же изменяемый объект, а затем мутация объекта. Все ссылки заканчиваются последним значением объекта, как и ожидалось.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню