Quelle est la façon pythonique pour éviter les paramètres par défaut qui sont des listes vides?

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

  •  21-08-2019
  •  | 
  •  

Question

Parfois, il semble naturel d'avoir un paramètre par défaut qui est une liste vide. Pourtant, Python donne un comportement inattendu dans ces situations .

Si, par exemple, j'ai une fonction:

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

La première fois qu'il est appelé par défaut fonctionnera, mais après les appels qui mettra à jour la liste existante (avec un « a » chaque appel) et imprimer la version mise à jour.

Alors, quelle est la manière pythonique pour obtenir le comportement que je désire (une nouvelle liste à chaque appel)?

Était-ce utile?

La solution

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

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

Les docs disent que vous devez utiliser comme valeur par défaut None et pour elle dans le corps du fonction.

Autres conseils

réponses existantes ont déjà fourni des solutions directes demandé. Cependant, étant donné que cela est un piège très courant pour les nouveaux programmeurs Python, il vaut la peine d'ajouter l'explication pourquoi python se comporte de cette façon, ce qui est bien résumée dans « le Guide Hitchhikers à Python » comme « mutables Arguments par défaut : " http://docs.python-guide.org/en/latest/writing/ gotchas /

Quote:. « arguments par défaut de Python sont évalués une fois lorsque la fonction est définie, pas à chaque fois que la fonction est appelée (comme il est à dire, Ruby) Cela signifie que si vous utilisez un argument par défaut mutable et muter elle, vous et ont muté cet objet pour tous les futurs appels à la fonction ainsi "

Exemple de code pour la mettre en œuvre:

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

Pas qu'il importe dans ce cas, mais vous pouvez utiliser l'identité d'objet pour tester Aucun:

if working_list is None: working_list = []

Vous pouvez également profiter de la façon dont l'opérateur booléen ou est défini en python:

working_list = working_list or []

Bien que cela se comportera de façon inattendue si l'appelant vous donne une liste vide (qui compte comme faux) que working_list et attend votre fonction de modifier la liste qu'il lui a donné.

Si l'intention de la fonction est de modify le paramètre passé comme working_list, voir la réponse de HenryR (= Aucun, vérifiez Aucun intérieur).

Mais si vous ne l'avez pas l'intention de muter l'argument, il suffit d'utiliser comme point de départ pour une liste, vous pouvez simplement le copier:

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

(ou dans ce cas simple juste, mais je suppose que print starting_list + ["a"] c'était juste un exemple de jouet)

En général, vos arguments est muter mauvais style en Python. Les seules fonctions comme on s'y attendait à muter un objet sont des méthodes de l'objet. Il est encore plus rare de muter un argument optionnel - est un effet secondaire qui ne se produit que dans certains appels vraiment la meilleure interface

  • Si vous le faites de l'habitude C des « arguments de sortie », qui est tout à fait inutile. - vous pouvez toujours revenir plusieurs valeurs comme tuple

  • Si vous faites cela pour construire efficacement une longue liste de résultats sans construire des listes intermédiaires, envisager l'écriture comme un générateur et d'utiliser lorsque vous result_list.extend(myFunc()) l'appeler. De cette façon, vos conventions d'appel reste très propre.

Un modèle où un arg en option muter souvent fait est un arg "mémo" caché dans les fonctions récursives:

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)

Je pourrais être hors-sujet, mais rappelez-vous que si vous voulez juste passer un nombre variable d'arguments, la façon pythonique est de passer un tuple ou un dictionnaire *args **kargs. Ceux-ci sont facultatives et sont mieux que la syntaxe myFunc([1, 2, 3]).

Si vous voulez passer un tuple:

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]

Si vous voulez passer un dictionnaire:

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

Il y a déjà eu une bonne et réponses fourni. Je voulais juste donner une autre syntaxe pour écrire ce que vous voulez faire que je trouve plus beau quand vous voulez, par exemple, créer une classe avec défaut des listes vides:

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

Cet extrait utilise le si d'autre syntaxe de l'opérateur. Je l'aime surtout parce qu'il est un peu une doublure soignée sans les deux points, etc. impliqués et il se lit presque comme une phrase anglaise normale. :)

Dans votre cas, vous pouvez écrire

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

Je pris la classe d'extension UCSC Python for programmer

Ce qui est vrai: def Fn (data = []):

  

a) est une bonne idée afin que vos listes de données commencent à vide à chaque appel.

     

b) est une bonne idée pour que tous les appels à la fonction qui ne fournissent pas d'arguments sur l'appel obtenir la liste vide sous forme de données.

     

c) est une idée raisonnable tant que vos données sont une liste de chaînes.

     

d) est une mauvaise idée, car la valeur par défaut [] accumule les données et la valeur par défaut [] va changer avec les appels suivants.

Réponse:

  

d) est une mauvaise idée, car la valeur par défaut [] accumule les données et la valeur par défaut [] va changer avec les appels suivants.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top