Вот упрощенная версия кода в вопросе:Почему построение нового объекта потенциально может изменить поле * каждого * экземпляра этого класса?
class thing:
def __init__(self, data1, data2={'foo': 1}):
self.data2 = data2
self.data2['bar'] = data1
datas = ['FIRST', 'SECOND']
things = [thing(x) for x in datas]
for p in things:
print p.data2['bar']
Я бы ожидать этот код для возврата:
FIRST
SECOND
Однако это фактически возвращается:
SECOND
SECOND
Почему?
Моя догадка:
- , что я создаю один словарь
ur_dict = {'foo': 1}
, - , что когда я инициализировать объект класса
thing
Я не создания нового словаряself.data2={'foo': 1}
но скорее инициализируя ссылку наur_dict
, - и что когда в конструкторе я добавляю пару ключ-значение
bar: data1
доself.data2
, я фактически добавляю этот ключ и значение вur_dict
. Это обновит полеdata2
каждого отдельного объекта в списке.
Я проверил эту гипотезу, добавив следующий фрагмент кода в приведенном выше коде:
things[0].data2['bar'] = 'FIRST'
for p in things:
print p.data2['bar']
Конечно, результат:
FIRST
FIRST
Так что я думаю, что моя гипотеза, вероятно, правильно, но это все еще таинственно для меня. Мой вопрос, кажется, очень связан с вопросом this --- когда я создаю значение по умолчанию в конструкторе, это значение a класс переменная вместо переменной экземпляра? Почему python не создает новые объекты для значений аргументов по умолчанию в объекте? Есть ли хороший способ концептуализировать или поймать такую ошибку в будущем? Каковы хорошие ссылки, чтобы узнать, что здесь происходит?
(Извиняюсь заранее, если я подогнаны жаргон, я математик по формальному образованию.)
Edit: @CrazyPython[::-1]
упоминается это свойство функций, а не классов или объектов. Я создал пример функции с изменяемым аргументом по умолчанию, и я пытаюсь понять, как разбить ее так, как я это испытал с объектами и классами выше. Придумал это:
EXPONENT = 1
def fooBar(x, y=EXPONENT):
return x**y
print EXPONENT
print foo(5)
EXPONENT = 2
print EXPONENT
print foo(5)
Однако, он возвращает:
1
5
2
5
Что такое хороший способ «сломать» это, чтобы показать, почему бы не использовать изменяемый аргумент по умолчанию здесь?
Ahh, «изменяемые аргументы по умолчанию» - это слова для этого явления. Я не уверен, что вы подразумеваете под «привязанным к функции», я отредактирую пример в моем вопросе. – Neal
@ Нил я уточнил. –
@Neal Это не имеет ничего общего с классами или экземплярами. Такое поведение возникает везде, где существует функция –