Qual é a maneira Python para evitar parâmetros predefinidos que são listas vazias?

StackOverflow https://stackoverflow.com/questions/366422

  •  21-08-2019
  •  | 
  •  

Pergunta

Às vezes parece natural ter um parâmetro padrão que é uma lista vazia. No entanto Python dá um comportamento inesperado nestas situações .

Se, por exemplo, eu tenho uma função:

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

A primeira vez que ele é chamado o padrão vai funcionar, mas as chamadas depois que irá atualizar a lista existente (com um "a" cada chamada) e imprimir a versão atualizada.

Então, o que é a maneira Python para obter o comportamento que eu desejo (uma lista fresca em cada chamada)?

Foi útil?

Solução

def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

Os docs dizer que você deve usar None como padrão e explicitamente para que no corpo da função.

Outras dicas

respostas existentes já forneceu as soluções diretas, como pediu. No entanto, uma vez que esta é uma armadilha muito comum para novos programadores Python, que vale a pena acrescentar a explicação porque python se comporta dessa maneira, o que é muito bem resumido em " Guia do Mochileiro Python " como " Argumentos mutáveis ??padrão ": http://docs.python-guide.org/en/latest/writing/ gotchas /

Citação: " argumentos padrão do Python são avaliados uma vez quando a função é definida, não cada vez que a função é chamada (como é em digamos, Ruby) Isto significa que se você usar um argumento padrão mutável e mutação. -lo, você vai e se transformaram esse objeto para todas as futuras chamadas para a função assim "

Exemplo de código para implementá-lo:

def foo(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

Não que importa neste caso, mas você pode usar a identidade do objeto de teste para Nenhum:

if working_list is None: working_list = []

Você também pode tirar proveito de como o operador booleano ou é definida em python:

working_list = working_list or []

Embora isso vai se comportar de forma inesperada se o chamador dá-lhe uma lista vazia (que conta como falso) como working_list e espera que a sua função para modificar a lista que ele deu.

Se a intenção da função é a Modificar o parâmetro passado como working_list, ver a resposta de HenryR (= None, cheque para Nenhum interior).

Mas se você não tinha a intenção de transformar o argumento, apenas usá-lo como ponto de partida para uma lista, você pode simplesmente copiá-lo:

def myFunc(starting_list = []):
    starting_list = list(starting_list)
    starting_list.append("a")
    print starting_list

(ou neste caso simples, apenas print starting_list + ["a"] mas eu acho que foi apenas um exemplo de brinquedo)

Em geral, transformando seus argumentos é ruim estilo em Python. As únicas funções que são totalmente esperados para mutar um objecto são métodos do objecto. É ainda mais raro de mutação um argumento opcional -? É um efeito colateral que acontece apenas em algumas chamadas realmente a melhor interface

  • Se você fazê-lo a partir do hábito C de "argumentos de saída", isso é completamente desnecessário -. Você sempre pode retornar vários valores como uma tupla

  • Se você fizer isso para construir de forma eficiente uma longa lista de resultados sem a construção de listas de intermediários, considere escrevê-lo como um gerador e usando result_list.extend(myFunc()) quando você está chamando. Desta forma o seu convenções de chamada continua a ser muito limpo.

Um padrão onde mutação opcional arg é feito com freqüência é uma escondido "memo" arg em funções recursivas:

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)

Eu poderia estar off-topic, mas lembre-se que se você apenas quer passar um número variável de argumentos, a maneira Python é passar um *args tupla ou uma **kargs dicionário. Estes são opcionais e são melhores do que o myFunc([1, 2, 3]) sintaxe.

Se você quer passar uma tupla:

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]

Se você quer passar um dicionário:

def myFunc(arg1, **kargs):
   print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}

Já houve bom e respostas corretas fornecidas. Eu só queria dar uma outra sintaxe para escrever o que você quer fazer o que eu acho mais bonito quando você, por exemplo, deseja criar uma classe com listas vazias padrão:

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 []

Este trecho faz uso da sintaxe se operador de outra pessoa. Eu gosto especialmente porque é um pouco de uma linha pura, sem vírgulas, etc. envolvidos e quase lê como uma sentença normal de Inglês. :)

No seu caso, você poderia escrever

def myFunc(working_list=None):
    working_list = [] if working_list is None else working_list
    working_list.append("a")
    print working_list

Eu levei o Python for programmer UCSC classe extensão

O que é verdade de: FN Def (data = []):

a) é uma boa idéia para que suas listas de dados começar vazio com cada chamada.

b) é uma boa idéia para que todas as chamadas para a função que não oferecem quaisquer argumentos na chamada terá a lista vazia como dados.

c) é uma idéia razoável contanto que seus dados é uma lista de strings.

d) é uma má idéia, porque o padrão [] irá acumular dados eo padrão [] vai mudar com as chamadas subsequentes.

Resposta:

d) é uma má idéia, porque o padrão [] irá acumular dados eo padrão [] vai mudar com as chamadas subsequentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top