Подтвердить что ты не робот

Что такое pythonic, чтобы избежать параметров по умолчанию, которые являются пустыми списками?

Иногда кажется естественным иметь параметр по умолчанию, который является пустым списком. Тем не менее Python дает неожиданное поведение в этих ситуациях.

Если, например, у меня есть функция:

def my_func(working_list = []):
    working_list.append("a")
    print(working_list)

При первом вызове он будет работать по умолчанию, но после этого вызовы обновят существующий список (по одному вызову "a") и напечатают обновленную версию.

Итак, каков питонный способ получить желаемое поведение (новый список при каждом вызове)?

4b9b3361

Ответ 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 [] будет меняться при последующих вызовах.