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.
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):
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!
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.
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).
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" )
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])
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)
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
.
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']
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.
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)
@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.
** 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)
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()