Que font **
(double étoile) et *
(étoile) pour les paramètres
Ils permettent aux fonctions à définir d'accepter et aux utilisateurs de passer n'importe quel nombre d'arguments, positional ( *
) et keyword ( **
).
Définition des fonctions
*args
permet un nombre illimité d'arguments positionnels (paramètres), qui seront attribués à un tuple nommé args
.
**kwargs
permet un nombre illimité d'arguments de mot-clé facultatifs (paramètres), qui seront dans un dict nommé kwargs
.
Vous pouvez (et devez) choisir n'importe quel nom approprié, mais si l'intention est que les arguments soient de sémantique non spécifique args
et kwargs
soient des noms standard.
Expansion, passage d'un nombre quelconque d'arguments
Vous pouvez également utiliser *args
et **kwargs
pour passer des paramètres à partir de listes (ou tout itérable) et de dict (ou de tout mappage), respectivement.
La fonction recevant les paramètres n'a pas besoin de savoir qu'ils sont développés.
Par exemple, xrange de Python 2 ne s'attend pas explicitement *args
, mais puisqu'il prend 3 entiers comme arguments:
>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x) # expand here
xrange(0, 2, 2)
Comme autre exemple, nous pouvons utiliser l'expansion de dict dans str.format
:
>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'
Nouveau dans Python 3: définition de fonctions avec des arguments de mots clés uniquement
Vous pouvez avoir des arguments de mot clé uniquement après que *args
- par exemple, ici, kwarg2
doit être donné comme argument de mot clé - et non positionnellement:
def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs):
return arg, kwarg, args, kwarg2, kwargs
Usage:
>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})
En outre, *
peut être utilisé par lui - même pour indiquer que les arguments que mot - clé suivent, sans tenir compte des arguments de position illimitée.
def foo(arg, kwarg=None, *, kwarg2=None, **kwargs):
return arg, kwarg, kwarg2, kwargs
Ici, kwarg2
encore une fois, doit être un argument de mot clé explicitement nommé:
>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})
Et nous ne pouvons plus accepter d'arguments positionnels illimités car nous n'avons pas *args*
:
>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments
but 5 positional arguments (and 1 keyword-only argument) were given
Encore une fois, plus simplement, nous devons ici kwarg
être donnés par un nom, et non par position:
def bar(*, kwarg=None):
return kwarg
Dans cet exemple, nous voyons que si nous essayons de passer kwarg
positionnellement, nous obtenons une erreur:
>>> bar('kwarg')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given
Nous devons explicitement passer le kwarg
paramètre comme argument de mot clé.
>>> bar(kwarg='kwarg')
'kwarg'
Démos compatibles Python 2
*args
(généralement **kwargs
appelés "star-args") et (les étoiles peuvent être impliquées en disant "kwargs", mais être explicites avec "double-star kwargs") sont des idiomes courants de Python pour utiliser la notation *
et **
. Ces noms de variables spécifiques ne sont pas requis (par exemple, vous pouvez utiliser *foos
et **bars
), mais un écart par rapport à la convention est susceptible de faire enrager vos collègues codeurs Python.
Nous les utilisons généralement lorsque nous ne savons pas ce que notre fonction va recevoir ou combien d'arguments nous pouvons passer, et parfois même lorsque nommer chaque variable séparément deviendrait très compliqué et redondant (mais c'est un cas où généralement explicite est mieux qu'implicite).
Exemple 1
La fonction suivante décrit comment ils peuvent être utilisés et illustre le comportement. Notez que l' b
argument nommé sera consommé par le deuxième argument positionnel avant:
def foo(a, b=10, *args, **kwargs):
'''
this function takes required argument a, not required keyword argument b
and any number of unknown positional arguments and keyword arguments after
'''
print('a is a required argument, and its value is {0}'.format(a))
print('b not required, its default value is 10, actual value: {0}'.format(b))
# we can inspect the unknown arguments we were passed:
# - args:
print('args is of type {0} and length {1}'.format(type(args), len(args)))
for arg in args:
print('unknown arg: {0}'.format(arg))
# - kwargs:
print('kwargs is of type {0} and length {1}'.format(type(kwargs),
len(kwargs)))
for kw, arg in kwargs.items():
print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
# But we don't have to know anything about them
# to pass them to other functions.
print('Args or kwargs can be passed without knowing what they are.')
# max can take two or more positional args: max(a, b, c...)
print('e.g. max(a, b, *args) \n{0}'.format(
max(a, b, *args)))
kweg = 'dict({0})'.format( # named args same as unknown kwargs
', '.join('{k}={v}'.format(k=k, v=v)
for k, v in sorted(kwargs.items())))
print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
dict(**kwargs), kweg=kweg))
Nous pouvons vérifier l'aide en ligne pour la signature de la fonction, avec help(foo)
, qui nous indique
foo(a, b=10, *args, **kwargs)
Appelons cette fonction avec foo(1, 2, 3, 4, e=5, f=6, g=7)
qui imprime:
a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns:
{'e': 5, 'g': 7, 'f': 6}
Exemple 2
Nous pouvons également l'appeler à l'aide d'une autre fonction, dans laquelle nous fournissons simplement a
:
def bar(a):
b, c, d, e, f = 2, 3, 4, 5, 6
# dumping every local variable into foo as a keyword argument
# by expanding the locals dict:
foo(**locals())
bar(100)
impressions:
a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns:
{'c': 3, 'e': 5, 'd': 4, 'f': 6}
Exemple 3: utilisation pratique dans les décorateurs
OK, peut-être que nous ne voyons pas encore l'utilitaire. Imaginez donc que vous ayez plusieurs fonctions avec du code redondant avant et / ou après le code de différenciation. Les fonctions nommées suivantes ne sont que des pseudo-codes à des fins d'illustration.
def foo(a, b, c, d=0, e=100):
# imagine this is much more code than a simple function call
preprocess()
differentiating_process_foo(a,b,c,d,e)
# imagine this is much more code than a simple function call
postprocess()
def bar(a, b, c=None, d=0, e=100, f=None):
preprocess()
differentiating_process_bar(a,b,c,d,e,f)
postprocess()
def baz(a, b, c, d, e, f):
... and so on
Nous pouvons peut-être gérer cela différemment, mais nous pouvons certainement extraire la redondance avec un décorateur, et donc notre exemple ci-dessous montre comment *args
et **kwargs
peut être très utile:
def decorator(function):
'''function to wrap other functions with a pre- and postprocess'''
@functools.wraps(function) # applies module, name, and docstring to wrapper
def wrapper(*args, **kwargs):
# again, imagine this is complicated, but we only write it once!
preprocess()
function(*args, **kwargs)
postprocess()
return wrapper
Et maintenant, chaque fonction encapsulée peut être écrite de manière beaucoup plus succincte, comme nous avons pris en compte la redondance:
@decorator
def foo(a, b, c, d=0, e=100):
differentiating_process_foo(a,b,c,d,e)
@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
differentiating_process_bar(a,b,c,d,e,f)
@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
differentiating_process_baz(a,b,c,d,e,f, g)
@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
differentiating_process_quux(a,b,c,d,e,f,g,h)
Et par affacturage notre code, *args
et **kwargs
nous permet de faire, nous réduisons les lignes de code, d' améliorer la lisibilité et la maintenabilité, et sont seuls endroits canoniques pour la logique de notre programme. Si nous devons changer une partie de cette structure, nous avons un endroit où effectuer chaque changement.