Qual é a maneira Python para evitar parâmetros predefinidos que são listas vazias?
-
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)?
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.