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() получает один и тот же экземпляр словаря, который начинается пустым. Это отвечает на вопрос?

Другие вопросы по тегам