Что такое pythonic, чтобы избежать параметров по умолчанию, которые являются пустыми списками?
Иногда кажется естественным иметь параметр по умолчанию, который является пустым списком. Тем не менее Python дает неожиданное поведение в этих ситуациях.
Если, например, у меня есть функция:
def my_func(working_list = []):
working_list.append("a")
print(working_list)
При первом вызове он будет работать по умолчанию, но после этого вызовы обновят существующий список (по одному вызову "a") и напечатают обновленную версию.
Итак, каков питонный способ получить желаемое поведение (новый список при каждом вызове)?
Ответы
Ответ 1
def my_func(working_list=None):
if working_list is None:
working_list = []
working_list.append("a")
print(working_list)
В документах сказано, что вы должны использовать None
как значение по умолчанию и явно проверить его в теле функции.
Ответ 2
Существующие ответы уже предоставили прямые решения в соответствии с просьбой. Тем не менее, поскольку это очень распространенная ошибка для новых программистов на Python, стоит добавить объяснение, почему python ведет себя таким образом, которое хорошо представлено в " Руководстве автостопом по Python " как " Изменяемые аргументы по умолчанию ": http://docs.python-guide.org/о/последняя/запись/подводные камни /
Цитата: "Аргументы Pythons по умолчанию вычисляются один раз при определении функции, а не каждый раз, когда вызывается функция (как, например, в Ruby). Это означает, что если вы используете изменяемый аргумент по умолчанию и изменяете его, вы будете иметь мутировал этот объект для всех будущих вызовов функции "
Пример кода для его реализации:
def foo(element, to=None):
if to is None:
to = []
to.append(element)
return to
Ответ 3
Не важно, в этом случае, но вы можете использовать идентификатор объекта для тестирования None:
if working_list is None: working_list = []
Вы также можете воспользоваться тем, как логический оператор или определяется в python:
working_list = working_list or []
Хотя это будет вести себя неожиданно, если вызывающий абонент предоставит вам пустой список (который считается как false) в качестве рабочего_каталога и ожидает, что ваша функция изменит список, который он ему дал.
Ответ 4
Если целью функции является изменение параметра, переданного как working_list
, см. Ответ HenryR (= Нет, проверьте наличие Нет внутри).
Но если вы не намеревались изменить аргумент, просто используйте его в качестве отправной точки для списка, вы можете просто скопировать его:
def myFunc(starting_list = []):
starting_list = list(starting_list)
starting_list.append("a")
print starting_list
(или в этом простом случае просто print starting_list + ["a"]
но я думаю, это был просто игрушечный пример)
В общем, изменение ваших аргументов - плохой стиль в Python. Единственные функции, которые, как ожидается, будут мутировать объект, - это методы объекта. Еще реже мутировать необязательный аргумент - действительно ли побочный эффект, который происходит только при некоторых вызовах, действительно лучший интерфейс?
-
Если вы делаете это из привычки C к "выходным аргументам", это совершенно не нужно - вы всегда можете вернуть несколько значений в виде кортежа.
-
Если вы делаете это для эффективного построения длинного списка результатов без создания промежуточных списков, result_list.extend(myFunc())
написать его как генератор и использовать result_list.extend(myFunc())
при его вызове. Таким образом, ваши соглашения о вызовах остаются очень чистыми.
Один из примеров, где часто делается мутирование необязательного аргумента, - это скрытый аргумент "мемо" в рекурсивных функциях:
def depth_first_walk_graph(graph, node, _visited=None):
if _visited is None:
_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)
Ответ 5
Я мог бы быть вне темы, но помните, что если вы просто хотите передать переменное количество аргументов, пифонический путь - передать кортеж *args
или словарь **kargs
. Они являются необязательными и лучше, чем синтаксис myFunc([1, 2, 3])
.
Если вы хотите передать кортеж:
def myFunc(arg1, *args):
print args
w = []
w += args
print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]
Если вы хотите передать словарь:
def myFunc(arg1, **kargs):
print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}
Ответ 6
Уже есть хорошие и правильные ответы. Я просто хотел дать еще один синтаксис, чтобы написать то, что вы хотите сделать, что я считаю более красивым, когда вы, например, хотите создать класс с пустыми списками по умолчанию:
class Node(object):
def __init__(self, _id, val, parents=None, children=None):
self.id = _id
self.val = val
self.parents = parents if parents is not None else []
self.children = children if children is not None else []
В этом фрагменте используется синтаксис if else. Мне это особенно нравится, потому что это аккуратный маленький лайнер без двоеточий и т.д., И он почти читается как обычное английское предложение.:)
В вашем случае вы можете написать
def myFunc(working_list=None):
working_list = [] if working_list is None else working_list
working_list.append("a")
print working_list
Ответ 7
Я взял UCSC класс расширения Python for programmer
Что верно для: def Fn (data = []):
а) хорошая идея, чтобы ваши списки данных начинались пустыми при каждом вызове.
б) хорошая идея, так что все вызовы функции, которые не предоставляют аргументов при вызове, получат пустой список в качестве данных.
в) разумная идея, если ваши данные представляют собой список строк.
г) плохая идея, потому что default [] будет накапливать данные, а default [] будет меняться при последующих вызовах.
Ответ:
г) плохая идея, потому что default [] будет накапливать данные, а default [] будет меняться при последующих вызовах.