Параметр python по умолчанию оценивается только один раз?
Я начинающий питон, читая "учебник python", он говорит, что если у нас есть функция:
def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)
Откроется
[1]
[1, 2]
[1, 2, 3]
Поскольку значение по умолчанию оценивается только один раз, а list - изменяемый объект. Я могу это понять.
И он говорит continue, если мы не хотим, чтобы по умолчанию были разделены между подзадачами, мы можем:
def f(a, L=None):
if L is None: #line 2
L = []
L.append(a)
return L
print f(1)
print f(2)
print f(3)
и это выведет:
[1]
[2]
[3]
Но почему? Как это объяснить. Мы знаем, что значение по умолчанию оценивается только once
, и когда мы вызываем f (2), L не является None и что if
(в строке 2) не может быть истинным, поэтому L.append(a) == [1, 2]. Могу ли я предположить, что значение по умолчанию снова оценивается для некоторой причины, но что такое "по какой-то причине", просто потому, что интерпретатор python видит if L is None: L = []
Ответы
Ответ 1
"Значение по умолчанию оценивается только один раз" не означает, что параметр со значением по умолчанию сохраняет значение между вызовами функции. Это означает, что выражение, которое вы указали (часть None
def f(a, L=None)
), оценивается один раз, а объект, который он приводит, хранится в скрытом месте и повторно используется, если при вызове не задано значение для этого параметра. Параметры по-прежнему reset для значения (по умолчанию или нет) при каждом вызове.
Ответ 2
Python передает параметры функциям по значению; Таким образом, для объектов переданное значение является ссылкой на объект, а не новой копией объекта.
Что, наряду со следующей частью официальных документов, помогло мне понять это лучше (акцент мой):
Значения параметров по умолчанию оцениваются [...] при выполнении определения функции. Это означает, что выражение оценивается один раз, когда функция определена, и что для каждого вызова используется одно и то же "предварительно вычисленное" значение. Это особенно важно понять, когда параметр по умолчанию является изменяемый объект, такой как список или словарь: если функция изменяет объект (например, добавив элемент в список), значение по умолчанию изменяется. [...] Путь к этому - использовать None как значение по умолчанию и явно проверить его в теле функции [...]
Объединяя все это:
Если вы определяете параметр по умолчанию для изменяемого объекта (например, []
), то "предварительно вычисленное" значение является ссылкой на этот объект, поэтому каждый вызов функции всегда ссылается на один и тот же объект, который затем может быть мутирован по нескольким вызовам функции.
Однако, поскольку None
является неизменным встроенным типом, "предварительно вычисленное" значение для значения по умолчанию None
просто таково. Таким образом, каждый раз, когда вы вызываете функцию, параметр будет None
.
Надеюсь, это поможет! Я действительно думаю, что учебник мог иметь лучшую формулировку, потому что сначала я был смущен этим.
Ответ 3
В вашем втором примере у вас есть переменная L
. Сначала L
относится к None
. Вы перепечатаете его в новый пустой список для каждого вызова, а затем измените этот новый список. Помните, что L = []
совпадает с L = list()
В вашем первом примере, однако, L устанавливается в новый список один раз в объявлении функции. L не reset до []
при каждом вызове функции. Таким образом, вы всегда мутируете один и тот же список.
Ответ 4
Я думаю, что это происходит, потому что список является изменяемым объектом, а значение None является неизменным.
Для первой функции переменная L находится вне среды функций (в определении функции), и она ссылается на пустой список. Затем вы вносите изменения в этот список в среду функций, но поскольку список изменен, переменная L, которая находится за пределами функциональной среды, ссылается на этот теперь мутированный список, и это изменение распространяется при каждом вызове функции.
Для второй функции переменная L также находится вне среды функций (в определении функции), но на этот раз она ссылается на None, которая неизменна. Теперь каждое изменение, которое вы выполняете в функциональной среде, не повлияет на то, что L означает вне среды функций. Переменная L внутри функциональной среды относится к чему-то другому по мере ее изменения. Во-первых, это относится к пустым спискам, а затем к списку, который получает добавленное к нему значение. Затем вы возвращаете этот список. При следующем вызове функции вы вызываете ее с переменной L, которая находится вне среды функций, которая не изменилась и по-прежнему относится к None.
Надеюсь, что это имеет смысл.
Ответ 5
Что происходит:
Когда вызывается функция python, она вычисляется в среде, в которой она была определена, а не среде, в которой она называлась, хотя вторая часть является вторичной (каламбур не предназначен) для ответа на ваш вопрос.
Аргументы по умолчанию оцениваются только один раз во время определения функции. Это создает закрытие. Подумайте о закрытии как функциональный код + среда, в которой была определена функция.
Итак, в этом случае, когда функция была определена, L
был назначен []
, и теперь каждый последующий вызов функции будет использовать это значение L
.
В учебнике также упоминается:
Значения по умолчанию оцениваются в точке определения функции в определяющей области (а определяющая область - часть замыкания вместе с кодом функции)
http://docs.python.org/2/tutorial/controlflow.html#default-argument-values