Un nombre variable d'arguments peut-il être passé à une fonction?

345

De manière similaire à l'utilisation de varargs en C ou C ++:

fn(a, b)
fn(a, b, c, d, ...)
David Sykes
la source
5
Je réfère l'honnêteté au podcast 53: itc.conversationsnetwork.org/shows/…
David Sykes
2
Je dois aller avec M. Lott sur celui-ci. Vous pouvez rapidement obtenir une réponse faisant autorité sur celle-ci dans les documents Python, et vous aurez une idée de ce qu'il y a d'autre dans les documents. Il est à votre avantage d'apprendre à connaître ces documents si vous prévoyez de travailler en Python.
Brian Neal,
@DavidSykes Le lien est rompu
Dave Liu

Réponses:

447

Oui. Vous pouvez utiliser *argscomme argument non mot-clé . Vous pourrez alors passer n'importe quel nombre d'arguments.

def manyArgs(*arg):
  print "I was called with", len(arg), "arguments:", arg

>>> manyArgs(1)
I was called with 1 arguments: (1,)
>>> manyArgs(1, 2, 3)
I was called with 3 arguments: (1, 2, 3)

Comme vous pouvez le voir, Python décompresse les arguments en un seul tuple avec tous les arguments.

Pour les arguments de mots clés, vous devez les accepter comme un argument réel distinct, comme indiqué dans la réponse de Skurmedel .

se détendre
la source
8
Aussi important ... on peut trouver un moment où ils doivent passer un nombre inconnu d'arguments à une fonction. Dans un cas comme celui-ci, appelez votre "manyArgs" en créant une liste appelée "args" et en la passant à manyArgs comme ceci "manyArgs (* args)"
wilbbe01
4
C'est proche, mais ce n'est malheureusement pas assez général: manyArgs(x = 3)échoue avec TypeError. La réponse de Skumedel montre la solution à cela. Le point clé est que la signature générale d'une fonction est f(*list_args, **keyword_args)(pas f(*list_args)).
Eric O Lebigot
2
Le type de argsest tuple. kwargssignifie args mot-clé . Le type de kwargsest dictionary.
RoboAlex
1
Juste for arg in args:pour l'itération des
arguments
228

Ajout au déroulement de la publication:

Vous pouvez également envoyer plusieurs arguments de valeur-clé.

def myfunc(**kwargs):
    # kwargs is a dictionary.
    for k,v in kwargs.iteritems():
         print "%s = %s" % (k, v)

myfunc(abc=123, efh=456)
# abc = 123
# efh = 456

Et vous pouvez mélanger les deux:

def myfunc2(*args, **kwargs):
   for a in args:
       print a
   for k,v in kwargs.iteritems():
       print "%s = %s" % (k, v)

myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123

Ils doivent être à la fois déclarés et appelés dans cet ordre, c'est-à-dire que la signature de fonction doit être * args, ** kwargs et appelée dans cet ordre.

Skurmedel
la source
2
Je ne sais pas comment vous avez réussi à faire fonctionner myfunc (abc = 123, def = 456), mais dans le mien (2.7), 'def' ne peut pas être passé dans cette fonction sans obtenir une SyntaxError. Je suppose que c'est parce que def a un sens en python. Essayez plutôt myfunc (abc = 123, fgh = 567). (Sinon, excellente réponse et merci!)
Dannid
@Dannid: Aucune idée non plus haha ​​... ne fonctionne pas non plus sur 2.6 ou 3.2. Je vais le renommer.
Skurmedel
Quand vous dites «appelé dans cet ordre», voulez-vous dire passé dans cet ordre? Ou autre chose?
Nikhil Prabhu
1
@NikhilPrabhu: Ouais. Les arguments des mots clés doivent arriver en dernier lorsque vous l'appelez. Ils viennent toujours en dernier si vous avez également des arguments autres que des mots clés.
Skurmedel
2
Pour Python 3: échangez simplement print aavec print(a)et kwargs.iteritems():avec kwargs.items().
MasterControlProgram
22

Si je peux me permettre, le code de Skurmedel est pour python 2; pour l' adapter à python 3, le changement iteritemsde itemset ajouter entre parenthèses à print. Cela pourrait empêcher des débutants comme moi de tomber sur: AttributeError: 'dict' object has no attribute 'iteritems'et de chercher ailleurs (par exemple, l' erreur «l'objet« dict »n'a pas d'attribut« iteritems »» en essayant d'utiliser write_shp () de NetworkX pourquoi cela se produit.

def myfunc(**kwargs):
for k,v in kwargs.items():
   print("%s = %s" % (k, v))

myfunc(abc=123, efh=456)
# abc = 123
# efh = 456

et:

def myfunc2(*args, **kwargs):
   for a in args:
       print(a)
   for k,v in kwargs.items():
       print("%s = %s" % (k, v))

myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123
calocedrus
la source
12

Ajout aux autres excellents messages.

Parfois, vous ne voulez pas spécifier le nombre d'arguments et souhaitez utiliser des clés pour eux (le compilateur se plaindra si un argument passé dans un dictionnaire n'est pas utilisé dans la méthode).

def manyArgs1(args):
  print args.a, args.b #note args.c is not used here

def manyArgs2(args):
  print args.c #note args.b and .c are not used here

class Args: pass

args = Args()
args.a = 1
args.b = 2
args.c = 3

manyArgs1(args) #outputs 1 2
manyArgs2(args) #outputs 3

Ensuite, vous pouvez faire des choses comme

myfuns = [manyArgs1, manyArgs2]
for fun in myfuns:
  fun(args)
Vladtn
la source
2
def f(dic):
    if 'a' in dic:
        print dic['a'],
        pass
    else: print 'None',

    if 'b' in dic:
        print dic['b'],
        pass
    else: print 'None',

    if 'c' in dic:
        print dic['c'],
        pass
    else: print 'None',
    print
    pass
f({})
f({'a':20,
   'c':30})
f({'a':20,
   'c':30,
   'b':'red'})
____________

le code ci-dessus sortira

None None None
20 None 30
20 red 30

Cela revient à passer des arguments variables au moyen d'un dictionnaire

BN Venkataraman
la source
3
C'est un code terrible. Beaucoup mieux serait:f = lambda **dic: ' '.join(dic.get(key, 'None') for key in 'abc')
métaperture
0

Une autre façon de procéder, outre les bonnes réponses déjà mentionnées, dépend du fait que vous pouvez passer des arguments nommés facultatifs par position. Par exemple,

def f(x,y=None):
    print(x)
    if y is not None:
        print(y)

Rendements

In [11]: f(1,2)
1
2

In [12]: f(1)
1
condition de transversalité
la source