np.array, как на значения по умолчанию атрибутов в Python ссылаются несколько экземпляров?
У меня есть класс с некоторым полем атрибута, который является массивом NumPy. Класс обладает способностью порождать новые экземпляры того же класса. Теперь моя проблема в том, что все созданные таким образом экземпляры, похоже, указывают на один и тот же атрибут поля. Поэтому, когда ожидаемый результат данного кода:
[0, 0]
[1, 0]
[0, 2]
это на самом деле дает:
[1, 2]
[1, 2]
[1, 2]
и когда я изменяю запись в a.field впоследствии, запись изменяется в атрибуте поля всех экземпляров
Кто-нибудь получил представление о том, как обойти это или что я ошибся в понимании здесь?
import numpy as np
class A:
def __init__(self, field=np.zeros(2)):
self.field=field
def setField(self, c, index):
if index<len(self.field):
self.field[index]=c
def multiply(self):
new_A=np.empty(2, dtype=object)
for n in range(0, 2, 1):
new_A[n]=A()
new_A[n].setField(n+1, n)
return new_A
a=A()
subs=a.multiply()
print a.field
print subs[0].field
print subs[1].field
2 ответа
Это распространенная "ошибка", с которой все сталкиваются при изучении Python. Это не имеет ничего общего с NumPy в частности. Например, обычный список даст вам такое же поведение.
>>> def f(x=[]):
... x.append(1)
... return x
...
>>> f()
[1]
>>> f()
[1, 1]
>>> f()
[1, 1, 1]
Грубо говоря, этот код интерпретируется примерно так:
f_default_arg_x = []
def f(x=f_default_arg_x):
x.append(1)
return x
Как только вы узнаете, что эта проблема существует, вы можете найти множество страниц, объясняющих проблему и рассказывающих, что с этим делать. (Например: http://effbot.org/zone/default-values.htm)
Обычное решение - просто использовать None
в качестве заполнителя для "укажите значение по умолчанию".
def f(x=None):
if x is None:
x = []
x.append(1)
return x
Вы не должны помещать массив в качестве аргумента по умолчанию, вместо этого вы должны определить его следующим образом:
class A:
def __init__(self, field=None):
if field is None:
field = np.zeros(2)
self.field=field
def setField(self, c, index):
if index<len(self.field):
self.field[index]=c
def multiply(self):
new_A=np.empty(2, dtype=object)
for n in range(0, 2, 1):
new_A[n]=A()
new_A[n].setField(n+1, n)
return new_A
Проверьте "Наименьшее удивление" в Python: изменяемый аргумент по умолчанию для подробного объяснения того, почему вы не должны использовать список или массив в качестве аргумента по умолчанию.