Um número variável de argumentos pode ser passado para uma função?

345

De maneira semelhante ao uso de varargs em C ou C ++:

fn(a, b)
fn(a, b, c, d, ...)
David Sykes
fonte
5
Refiro a honrosa importância ao podcast 53: itc.conversationsnetwork.org/shows/…
David Sykes
2
Eu tenho que ir com o Sr. Lott neste. Você pode obter rapidamente uma resposta autorizada sobre essa questão nos documentos do Python, além de ter uma ideia do que mais existe nos documentos. É do seu benefício conhecer esses documentos se você planeja trabalhar em Python.
27609 Brian Neal
@DavidSykes Link quebrado
Dave Liu

Respostas:

447

Sim. Você pode usar *argscomo um argumento que não é de palavra-chave . Você poderá passar qualquer número de argumentos.

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)

Como você pode ver, o Python descompactará os argumentos como uma única tupla com todos os argumentos.

Para argumentos de palavras-chave, você precisa aceitá-los como um argumento real separado, conforme mostrado na resposta de Skurmedel .

descontrair
fonte
8
Também importante ... pode-se encontrar um momento em que eles precisam passar um número desconhecido de argumentos para uma função. Em um caso como esse chamado o seu "manyArgs" através da criação de uma lista chamada "args" e passando isso para manyArgs assim "manyArgs (* args)"
wilbbe01
4
Isso está próximo, mas infelizmente não é geral o suficiente: manyArgs(x = 3)falha com TypeError. A resposta de Skumedel mostra a solução para isso. O ponto principal é que a assinatura geral de uma função é f(*list_args, **keyword_args)(não f(*list_args)).
Eric O Lebigot
2
O tipo de argsé tuple. kwargssignifica palavra-chave args . O tipo de kwargsé dictionary.
RoboAlex 4/02/19
11
Apenas for arg in args:para iterar argumentos passados
MasterControlProgram
228

Adicionando à postagem de desenrolamentos:

Você também pode enviar vários argumentos de valor-chave.

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

E você pode misturar os dois:

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

Eles devem ser declarados e chamados nessa ordem, ou seja, a assinatura da função precisa ser * args, ** kwargs e chamada nessa ordem.

Skurmedel
fonte
2
Não tenho certeza de como o myfunc (abc = 123, def = 456) funcionou, mas no meu (2.7), 'def' não pode ser passado nessa função sem obter um SyntaxError. Suponho que isso seja porque def tem significado em python. Tente myfunc (abc = 123, fgh = 567). (Caso contrário, grande resposta e obrigado por isso!)
Dannid
@ Dannid: Nenhuma idéia também haha ​​... não funciona em 2.6 ou 3.2 também. Eu vou mudar o nome.
Skurmedel
Quando você diz 'chamado nessa ordem', você quer dizer passado nessa ordem? Ou alguma outra coisa?
Nikhil Prabhu
11
@NikhilPrabhu: Sim. Os argumentos de palavra-chave devem vir por último quando você o chamar. Eles sempre vêm por último se você tiver argumentos que não sejam de palavra-chave na chamada também.
Skurmedel
2
Para Python 3: Basta trocar print acom print(a)e kwargs.iteritems():com kwargs.items().
MasterControlProgram
22

Se eu puder, o código de Skurmedel é para python 2; adaptá-lo para python 3, a mudança iteritemspara itemse adicionar parênteses para print. Isso poderia impedir que iniciantes como eu encontrassem: AttributeError: 'dict' object has no attribute 'iteritems'e pesquisassem em outro lugar (por exemplo, o erro “objeto dict 'não tem atributo' iteritems '” ao tentar usar o write_shp () do NetworkX por que isso está acontecendo.

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

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

e:

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
fonte
12

Adicionando aos outros excelentes posts.

Às vezes, você não deseja especificar o número de argumentos e deseja usar chaves para eles (o compilador reclamará se um argumento transmitido em um dicionário não for usado no método).

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

Então você pode fazer coisas como

myfuns = [manyArgs1, manyArgs2]
for fun in myfuns:
  fun(args)
Vladtn
fonte
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'})
____________

o código acima será exibido

None None None
20 None 30
20 red 30

Isso é tão bom quanto passar argumentos variáveis ​​por meio de um dicionário

BN Venkataraman
fonte
3
Este é um código terrível. Muito melhor seria:f = lambda **dic: ' '.join(dic.get(key, 'None') for key in 'abc')
metaperture
0

Outra maneira de fazer isso, além das respostas legais já mencionadas, depende do fato de que você pode passar argumentos nomeados opcionais por posição. Por exemplo,

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

Rendimentos

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

In [12]: f(1)
1
condição de transversalidade
fonte