2016-08-23 3 views
3

Вот упрощенная версия кода в вопросе:Почему построение нового объекта потенциально может изменить поле * каждого * экземпляра этого класса?

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 

Что такое хороший способ «сломать» это, чтобы показать, почему бы не использовать изменяемый аргумент по умолчанию здесь?

ответ

2

self.data2={'foo': 1} - изменяемый аргумент по умолчанию. Никогда не делай этого. 99% времени это ошибка.

def foo(a, b={}): 
    ... 

не эквивалентен

def foo(a, b=None): 
    if b is None: 
     b = {} 

b не реконструирован каждый раз. Это тот же объект.

Есть ли хороший способ концептуализировать или поймать подобную ошибку в будущем?

Используйте PyCharm или другой хороший редактор

когда я создаю значение по умолчанию в конструкторе, это значение класса переменной вместо переменной экземпляра?

нет нет нет нет нет нет. Он привязан к функции, а не к классу или экземпляру. Это поведение относится ко всем функциям. Это не имеет ничего общего с классами или вашим связанным вопросом.

Почему python не создает новые объекты для значений аргументов по умолчанию в объекте?

Производительность и совместимость. Построение объекта является потенциально дорогостоящей операцией.


Добавление

  • Пожалуйста, используйте CamelCase для классов. Это не нарушение PEP 8, которое является официальным руководством стиля Python.
  • Получите достойный редактор. Лично я рекомендую PyCharm. (Я не продавец) Это бесплатно.
    • PyCharm может переименовывать переменные, выделять ошибки PEP 8, выполнять расширенные автозаполнения, проверять тип, спотовые переменные аргументы по умолчанию и многое другое.
  • Вы не наследуете object. That's bad.*

    Почему этот питон код изменяет поле каждый объекта в списке?

  • Без контекста это плохой заголовок. Он спрашивает о коде. Когда мы читаем название, у нас нет кода.


делает его более ясным

Когда функция указывается, аргументы по умолчанию вычисляются. (не когда он выполняется, когда он указан). Они привязаны к функции. Когда функция выполняется, копия аргументов по умолчанию не создается.Вы напрямую изменяете аргументы по умолчанию. Следующая функция, которая вызывается, получит тот же объект с изменениями.

* Вы, кажется, быть профессором или тому подобное. Пожалуйста, не обижайтесь на мему. Это просто риск получить советы из Интернета.

+1

Ahh, «изменяемые аргументы по умолчанию» - это слова для этого явления. Я не уверен, что вы подразумеваете под «привязанным к функции», я отредактирую пример в моем вопросе. – Neal

+0

@ Нил я уточнил. –

+1

@Neal Это не имеет ничего общего с классами или экземплярами. Такое поведение возникает везде, где существует функция –