web-dev-qa-db-fra.com

Meilleure pratique pour définir la valeur par défaut d'un paramètre censé être une liste en Python?

J'ai une fonction Python qui prend une liste comme paramètre. Si je mets la valeur par défaut du paramètre sur une liste vide comme celle-ci:

def func(items=[]):
    print items

Pylint me disait "valeur par défaut dangereuse [] comme argument". Je me demandais donc quelle est la meilleure pratique ici?

63
Jack Z

Utilisez None comme valeur par défaut:

def func(items=None):
    if items is None:
        items = []
    print items

Le problème avec un argument par défaut mutable est qu'il sera partagé entre toutes les invocations de la fonction - voir l '"avertissement important" dans le section pertinente du tutoriel Python =.

85
Sven Marnach

Je viens de rencontrer cela pour la première fois, et ma pensée immédiate est "eh bien, je ne veux pas muter la liste de toute façon, donc ce que je veux vraiment, c'est par défaut une liste immuable donc Python = me donnera une erreur si je mute accidentellement. "Une liste immuable est juste un tuple. Donc:

 def func (items = ()): 
 imprimer les items 

Bien sûr, si vous le transmettez à quelque chose qui veut vraiment une liste (par exemple isinstance (articles, liste)), cela vous posera des problèmes. Mais c'est quand même une odeur de code.

8
sfink

Pour un objet mutable comme paramètre par défaut dans les déclarations de fonction et de méthode, le problème est que l'évaluation et la création ont lieu exactement au même moment. L'analyseur python lit la tête de fonction et l'évalue au même moment.

La plupart des débutants supposent qu'un nouvel objet est créé à chaque appel, mais ce n'est pas correct! UN objet (dans votre exemple une liste) est créé au moment de la DÉCLARATION et non à la demande lorsque vous appelez la méthode.

Pour les objets imuables, ce n'est pas un problème, car même si tous les appels partagent le même objet, il est imuable et donc ses propriétés restent les mêmes.

Par convention, vous utilisez l'objet None pour les valeurs par défaut pour indiquer l'utilisation d'une initialisation par défaut, qui peut maintenant avoir lieu dans le corps de fonction, qui est naturellement évalué au moment de l'appel.

3
Don Question

De plus et aussi pour mieux comprendre ce qu'est python, voici mon petit extrait thématique:

from functools import wraps
def defaultFactories(func):
    'wraps function to use factories instead of values for defaults in call'
    defaults = func.func_defaults
    @wraps(func)
    def wrapped(*args,**kwargs):
        func.func_defaults = Tuple(default() for default in defaults)
        return func(*args,**kwargs)
    return wrapped

def f1(n,b = []):
    b.append(n)
    if n == 1: return b
    else: return f1(n-1) + b

@defaultFactories
def f2(n,b = list):
    b.append(n)
    if n == 1: return b
    else: return f2(n-1) + b

>>> f1(6)
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
>>> f2(6)
[1, 2, 3, 4, 5, 6]
1
Odomontois