web-dev-qa-db-fra.com

Manière d'utiliser ** kwargs en Python

Quelle est la bonne façon d'utiliser **kwargs en Python en ce qui concerne les valeurs par défaut?

kwargs renvoie un dictionnaire, mais quel est le meilleur moyen de définir les valeurs par défaut, ou existe-t-il? Devrais-je simplement y accéder sous forme de dictionnaire? Utiliser la fonction get? 

class ExampleClass:
    def __init__(self, **kwargs):
        self.val = kwargs['val']
        self.val2 = kwargs.get('val2')

Une question simple, mais sur laquelle je ne trouve pas de bonnes ressources. Les gens le font de différentes manières dans le code que j'ai vu et il est difficile de savoir quoi utiliser.

372
Kekoa

Vous pouvez transmettre une valeur par défaut à get() pour les clés ne figurant pas dans le dictionnaire:

self.val2 = kwargs.get('val2',"default value")

Toutefois, si vous envisagez d'utiliser un argument particulier avec une valeur par défaut particulière, pourquoi ne pas utiliser des arguments nommés en premier lieu?

def __init__(self, val2="default value", **kwargs):
377
balpha

Alors que la plupart des réponses disent que, par exemple,

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...etc...

est le même que"

def f(foo=None, bar=None, **kwargs):
    ...etc...

ce n'est pas vrai. Dans ce dernier cas, f peut être appelé en tant que f(23, 42), alors que le premier cas accepte les arguments nommés uniquement - aucun appel de position. Souvent, vous souhaitez laisser à l'appelant une flexibilité maximale. Par conséquent, le second formulaire, comme le stipulent la plupart des réponses, est préférable: ce n'est pas toujours le cas. Lorsque vous acceptez de nombreux paramètres facultatifs dont seuls quelques-uns sont passés, il peut s'avérer une excellente idée (éviter les accidents et le code illisible sur vos sites d'appels!) Pour forcer l'utilisation d'arguments nommés - threading.Thread est un exemple. La première forme est la façon dont vous implémentez cela dans Python 2.

L'idiome est tellement important qu'en Python 3, il a maintenant une syntaxe spécifique: chaque argument après un seul * dans la signature def est réservé à un mot-clé, c'est-à-dire qu'il ne peut pas être transmis comme argument de position, mais uniquement comme nom nommé. Donc, en Python 3, vous pouvez coder ce qui précède comme suit:

def f(*, foo=None, bar=None, **kwargs):
    ...etc...

En effet, dans Python 3, vous pouvez même avoir des arguments ne contenant que des mots-clés que ne sont pas facultatifs (sans valeur par défaut).

Cependant, Python 2 a encore de longues années de vie productive, il est donc préférable de ne pas oublier les techniques et les idiomes qui vous permettent de mettre en œuvre dans Python 2 des idées de conception importantes directement prises en charge par Python 3!

247
Alex Martelli

Je suggère quelque chose comme ça

def testFunc( **kwargs ):
    options = {
            'option1' : 'default_value1',
            'option2' : 'default_value2',
            'option3' : 'default_value3', }

    options.update(kwargs)
    print options

testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}

testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}

Et utilisez ensuite les valeurs comme vous le souhaitez

dictionaryA.update(dictionaryB) ajoute le contenu de dictionaryB à dictionaryA en remplaçant les clés dupliquées.

62
Abhinav Gupta

Tu ferais

self.attribute = kwargs.pop('name', default_value)

ou

self.attribute = kwargs.get('name', default_value)

Si vous utilisez pop, vous pouvez vérifier si des valeurs parasites ont été envoyées et prendre les mesures appropriées (le cas échéant).

52
Vinay Sajip

Utiliser ** kwargs et les valeurs par défaut est facile. Parfois, cependant, vous ne devriez pas utiliser ** kwargs en premier lieu.

Dans ce cas, nous ne faisons pas vraiment le meilleur usage de ** kwargs.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = kwargs.get('val',"default1")
        self.val2 = kwargs.get('val2',"default2")

Ce qui précède est un "pourquoi déranger?" déclaration. C'est pareil que

class ExampleClass( object ):
    def __init__(self, val="default1", val2="default2"):
        self.val = val
        self.val2 = val2

Lorsque vous utilisez ** kwargs, vous voulez dire qu'un mot clé n'est pas simplement optionnel, mais conditionnel. Il existe des règles plus complexes que les simples valeurs par défaut.

Lorsque vous utilisez ** kwargs, vous voulez dire quelque chose de plus semblable à ce qui suit, où les valeurs par défaut simples ne s'appliquent pas.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = "default1"
        self.val2 = "default2"
        if "val" in kwargs:
            self.val = kwargs["val"]
            self.val2 = 2*self.val
        Elif "val2" in kwargs:
            self.val2 = kwargs["val2"]
            self.val = self.val2 / 2
        else:
            raise TypeError( "must provide val= or val2= parameter values" )
39
S.Lott

Puisque **kwargs est utilisé lorsque le nombre d'arguments est inconnu, pourquoi ne pas le faire?

class Exampleclass(object):
  def __init__(self, **kwargs):
    for k in kwargs.keys():
       if k in [acceptable_keys_list]:
          self.__setattr__(k, kwargs[k])
25
srhegde

Voici une autre approche:

def my_func(arg1, arg2, arg3):
    ... so something ...

kwargs = {'arg1': 'Value One', 'arg2': 'Value Two', 'arg3': 'Value Three'}
# Now you can call the function with kwargs like this:

my_func(**kwargs)
13
orokusaki

Suivi de @srhegde suggestion d’utiliser setattr :

class ExampleClass(object):
    __acceptable_keys_list = ['foo', 'bar']

    def __init__(self, **kwargs):
        [self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]

Cette variante est utile lorsque la classe devrait avoir tous les éléments de notre liste acceptable.

10
rebelliard

Tu pourrais faire quelque chose comme ça

class ExampleClass:
    def __init__(self, **kwargs):
        arguments = {'val':1, 'val2':2}
        arguments.update(kwargs)
        self.val = arguments['val']
        self.val2 = arguments['val2']
10
Steef

Je pense que la bonne façon d'utiliser **kwargs en Python en ce qui concerne les valeurs par défaut consiste à utiliser la méthode du dictionnaire setdefault, comme indiqué ci-dessous:

class ExampleClass:
    def __init__(self, **kwargs):
        kwargs.setdefault('val', value1)
        kwargs.setdefault('val2', value2)

De cette façon, si un utilisateur passe 'val' ou 'val2' dans le mot clé args, ils seront utilisés. sinon, les valeurs par défaut définies seront utilisées.

10
user1930143

Si vous voulez combiner cela avec * args, vous devez conserver * args et ** kwargs à la fin de la définition.

Alors:

def method(foo, bar=None, *args, **kwargs):
    do_something_with(foo, bar)
    some_other_function(*args, **kwargs)
2
Jens Timmerman

@AbhinavGupta et @Steef ont suggéré d'utiliser update(), que j'ai trouvé très utile pour le traitement de listes d'arguments volumineuses:

args.update(kwargs)

Que faire si nous voulons vérifier que l'utilisateur n'a pas transmis d'argument parasite/non pris en charge? @VinaySajip a souligné que pop() peut être utilisé pour traiter de manière itérative la liste des arguments. Ensuite, tous les arguments restants sont parasites. Agréable.

Voici un autre moyen de le faire, qui conserve la syntaxe simple consistant à utiliser update():

# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments

# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
    raise TypeError('Unknown arguments: {}'.format(unknown_args))

# Update args to contain user-supplied arguments
args.update(kwargs)

unknown_args est une set contenant les noms des arguments qui ne figurent pas dans les valeurs par défaut.

1
user20160

** kwargs donne la liberté d'ajouter n'importe quel nombre d'arguments de mots clés. On peut avoir une liste de clés pour lesquelles il peut définir des valeurs par défaut. Mais définir les valeurs par défaut pour un nombre indéterminé de clés semble inutile. Enfin, il peut être important d’avoir les clés comme attributs d’instance. Donc, je le ferais comme suit:

class Person(object):
listed_keys = ['name', 'age']

def __init__(self, **kwargs):
    _dict = {}
    # Set default values for listed keys
    for item in self.listed_keys: 
        _dict[item] = 'default'
    # Update the dictionary with all kwargs
    _dict.update(kwargs)

    # Have the keys of kwargs as instance attributes
    self.__dict__.update(_dict)
0
user3503692

Une autre solution simple pour traiter des arguments inconnus ou multiples peut être:

class ExampleClass(object):

    def __init__(self, x, y, **kwargs):
      self.x = x
      self.y = y
      self.attributes = kwargs

    def SomeFunction(self):
      if 'something' in self.attributes:
        dosomething()
0
tmsss