Значения по умолчанию в python
Nov 1, 2014 16:55 · 277 words · 2 minute read
Проходил как-то собеседование на должность python-разработчика, одним из вопросов был что выведет следующий код:
def f(value=[]):
value.append(1)
print value
f()
f([1])
f()
К сожалению, хоть я и знал в чём подвох (значения по умолчанию в python не так уж просты :) ), но ответил неправильно. Что же тут не так можно посмотреть под катом.
Всё дело тут в т.н. значении по умолчанию, которое на самом деле не совсем уж значение, а очень даже переменная. Фишка в том, что при разборе кода компилятор вычисляет значение (в данном случае []) и записывает в value, которая определена в области видимости функции f. В дельнейшем, если оная будет вызываться с параметром, то в value будет использоваться параметр, в противном случае - значение по умолчанию, которое получили ранее. Но! Оно меняется… Сам в шоке :) а всё потому, что константным остаётся ссылка, т.е. между вызовами f value сохраняет своё состояние. Убедиться в этом можно на примере пошаговово выполнения:
Итак, проблема ясна, а как же выкручиваться? Первое, что приходит в голову - не трогать значение по умолчанию и сохранять его в какую-нибудь другую переменную:
Упс, не помогло :( Видимо, придётся каждый раз полностью копировать исходное значение:
Что там Гвидо говорил: “Явное лучше неявного”? :) Ну-ну…
UPD
И вот снова наступил на эти грабли уже в Django. Была задача сделать выпадающий список с элементами из базы данных. Естественно, первое, что пришло в голову:
some_field = forms.ChoiceField(choices=get_choices())
Но вот выполнение get_choices происходит только один раз - при запуске приложения. К счастью, я не одинок в своей проблеме, в интернете нашлось “решение” (скорее костыль) через инициализацию этого поля в конструкторе:
class MyCustomForm(forms.Form):
other_field = forms.CharField()
...
def __init__(self, *args, **kwargs):
super(MyCustomForm, self).__init__(*args, **kwargs)
self.fields["some_field"] = forms.ChoiceField(choices=get_choices())