Python - входящий исходящий аргумент
Я читал в Expert Python Programming об этом крайнем случае. Проверьте этот код:
def f(arg={}):
arg['3'] = 4
return arg
>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4, '4': 'Still here'}
Мне не понятно, почему, когда f
вызывается в последний раз (после того, как его возвращаемое значение было сохранено), вместо присвоения arg пустого dict (так как он был вызван без аргументов), он сохраняет старую ссылку.
В книге говорится так: "если объект создан в аргументах, ссылка на аргумент будет по-прежнему жива, если функция вернет объект".
Я понимаю, что "это так работает", но почему так?
3 ответа
Поскольку аргументы по умолчанию оцениваются только один раз, когда функция оценивается и создается (они являются частью определения функции и могут быть получены, например, через inspect.getargspec).
Поскольку они являются частью функции, каждый вызов функции будет иметь один и тот же экземпляр значения по умолчанию. Это не проблема, если это неизменное значение, но как только оно изменчиво, оно может стать ошибкой.
Та же самая "особенность" существует в определениях классов, учитывая определение классов:
class A(object):
foo = {}
призвание
x = A()
y = A()
x.foo['bar'] = "baz"
... дал бы y.foo['bar'] значение "baz", так как x и y имеют одинаковый foo. Вот почему инициализация члена должна выполняться в init вместо тела класса.
Ваша проблема по умолчанию к изменчивому аргументу (словарь в этом случае):
def f(arg={}):
arg['3'] = 4
return arg
должно быть:
def f(arg=None):
arg = arg if arg is not None else {}
arg['3'] = 4
return arg
выходы:
>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4}
как и следовало ожидать.
Проблема здесь заключается в том, что аргументы по умолчанию оцениваются, когда функция впервые определяется / анализируется, а не когда они вызываются. Это просто нюанс анализатора Python, о котором вам нужно знать.
Почему, посмотрите "Наименее удивление" и изменяемый аргумент по умолчанию
Параметр по умолчанию создается один раз, когда объявляется функция, поэтому каждый вызов функции f() получает один и тот же экземпляр словаря, который начинается пустым. Это отвечает на вопрос?