How can I avoid issues caused by Python's early-bound default parameters (e.g. mutable default arguments "remembering" old data)?
Как я могу избежать проблем, вызванных параметрами по умолчанию с ранней привязкой к Python (например, изменяемыми аргументами по умолчанию, "запоминающими" старые данные)?
При первом вызове по умолчанию будет работать, но последующие вызовы обновят существующий список (по одному "a" каждому вызову) и напечатают обновленную версию.
Как я могу исправить функцию так, чтобы, если она вызывается повторно без явного аргумента, каждый раз использовался новый пустой список?
Переведено автоматически
Ответ 1
defmy_func(working_list=None): if working_list isNone: working_list = []
# alternative: # working_list = [] if working_list is None else working_list
working_list.append("a") print(working_list)
В документах говорится, что вы должны использовать None значение по умолчанию и явно проверять его в теле функции.
Ответ 2
В других ответах уже были предоставлены прямые решения, как и просили, однако, поскольку это очень распространенная ошибка для начинающих программистов на Python, стоит добавить объяснение того, почему Python ведет себя таким образом, которое хорошо изложено в Руководстве автостопщиков по Python в разделе Изменяемые аргументы по умолчанию:
Аргументы по умолчанию в Python оцениваются один раз при определении функции, а не при каждом вызове функции (как, скажем, в Ruby). Это означает, что если вы используете изменяемый аргумент по умолчанию и изменяете его, вы будете изменять этот объект и для всех будущих вызовов функции.
Ответ 3
Не то чтобы это имело значение в данном случае, но вы можете использовать object identity для проверки отсутствия:
if working_list isNone: working_list = []
Вы также могли бы воспользоваться тем, как в python определен логический оператор or:
working_list = working_list or []
Хотя это приведет к неожиданному поведению, если вызывающий объект предоставит вам пустой список (который считается ложным) в качестве working_list и ожидает, что ваша функция изменит список, который он ей предоставил.
Ответ 4
Если целью функции является изменение параметра, переданного как working_list, смотрите Ответ Генри (= Нет, проверьте, нет ли внутри).
Но если вы не собирались изменять аргумент, просто используйте его как отправную точку для списка, вы можете просто скопировать его:
(или в этом простом случае просто print starting_list + ["a"] но я думаю, что это был просто игрушечный пример)
В общем, изменять ваши аргументы - плохой стиль в Python. Единственные функции, которые, как ожидается, полностью изменят объект, - это методы объекта. Еще реже изменять необязательный аргумент — действительно ли побочный эффект, который возникает только в некоторых вызовах, является лучшим интерфейсом?
Если вы делаете это по привычке C "выводить аргументы", это совершенно не нужно - вы всегда можете вернуть несколько значений в виде кортежа.
Если вы делаете это для эффективного построения длинного списка результатов без создания промежуточных списков, подумайте о написании его как генератора и использовании result_list.extend(myFunc()) при его вызове. Таким образом, ваши соглашения о вызовах остаются очень чистыми.
Один шаблон, в котором часто выполняется изменение необязательного аргумента , - это скрытый аргумент "memo" в рекурсивных функциях:
defdepth_first_walk_graph(graph, node, _visited=None): if _visited isNone: _visited = set() # create memo once in top-level call
if node in _visited: return _visited.add(node) for neighbour in graph[node]: depth_first_walk_graph(graph, neighbour, _visited)