Czy do funkcji można przekazać zmienną liczbę argumentów?


345

W podobny sposób jak przy użyciu varargs w C lub C ++:

fn(a, b)
fn(a, b, c, d, ...)

5
Honorową loterię odsyłam do podcastu 53: itc.conversationsnetwork.org/shows/…
David Sykes

2
W tej sprawie muszę iść z panem Lottem. Możesz szybko uzyskać autorytatywną odpowiedź na to pytanie w dokumentach Pythona, a także poczuć, co jeszcze jest w dokumentach. Dla twojej korzyści jest poznanie tych dokumentów, jeśli planujesz pracować w Pythonie.
Brian Neal

@DavidSykes Link jest zepsuty
Dave Liu

Odpowiedzi:


447

Tak. Możesz użyć *argsjako argumentu innego niż słowo kluczowe . Będziesz wtedy mógł przekazać dowolną liczbę argumentów.

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)

Jak widać, Python rozpakuje argumenty jako pojedynczą krotkę ze wszystkimi argumentami.

W przypadku argumentów słów kluczowych należy je zaakceptować jako osobny rzeczywisty argument, jak pokazano w odpowiedzi Skurmedela .


8
Również ważne ... może się zdarzyć, że trzeba przekazać nieznaną liczbę argumentów do funkcji. W takim przypadku nazywaj „manyArgs”, tworząc listę o nazwie „args” i przekazując ją do wieluArgs, takich jak ten „manyArgs (* args)”
wilbbe01

4
Jest blisko, ale niestety nie jest to wystarczająco ogólne: manyArgs(x = 3)zawodzi TypeError. Odpowiedź Skumedela pokazuje rozwiązanie tego problemu. Kluczową kwestią jest to, że ogólna sygnatura funkcji to f(*list_args, **keyword_args)(nie f(*list_args)).
Eric O Lebigot,

2
Rodzaj argsjest tuple. kwargsoznacza słowo kluczowe args . Rodzaj kwargsjest dictionary.
RoboAlex,

1
Tylko for arg in args:do iteracji przez przekazane argumenty
MasterControlProgram

228

Dodawanie do postów odwijających:

Możesz również wysyłać wiele argumentów klucz-wartość.

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

I możesz mieszać dwa:

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

Muszą być zarówno deklarowane, jak i wywoływane w tej kolejności, tzn. Sygnatura funkcji musi być * args, ** kwargs i wywoływana w tej kolejności.


2
Nie jestem pewien, w jaki sposób sprawiłeś, że myfunc (abc = 123, def = 456) działało, ale w moim (2.7), 'def' nie może zostać przekazane w tej funkcji bez otrzymania błędu SyntaxError. Zakładam, że dzieje się tak, ponieważ def ma znaczenie w pythonie. Spróbuj zamiast tego myfunc (abc = 123, fgh = 567). (W przeciwnym razie świetna odpowiedź i dzięki za to!)
Dannid

@Dannid: Nie mam pojęcia, że ​​haha ​​... nie działa również w wersjach 2.6 i 3.2. Zmienię nazwę.
Skurmedel

Kiedy mówisz „wezwany w tej kolejności”, masz na myśli przekazany w tej kolejności? Albo coś innego?
Nikhil Prabhu,

1
@NikhilPrabhu: Tak. Argumenty słów kluczowych muszą pojawiać się na końcu, gdy je wywołujesz. Zawsze pojawiają się na końcu, jeśli w wywołaniu są również argumenty inne niż słowa kluczowe.
Skurmedel

2
Dla Pythona 3: Wymiana Sprawiedliwego print az print(a), i kwargs.iteritems():z kwargs.items().
MasterControlProgram

22

Jeśli mogę, kod Skurmedela dotyczy Pythona 2; w celu dostosowania go do Python 3, zmiany iteritemsdo itemsi dodać do nawiasu print. To może zapobiec wpadnięciu na początkujących takich jak ja: AttributeError: 'dict' object has no attribute 'iteritems'i szukaniu gdzie indziej (np. Błąd „obiekt„ dict ”nie ma atrybutu„ iteritems ”” podczas próby użycia metody write_shp () ) przez NetworkX, dlaczego tak się dzieje.

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

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

i:

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

12

Dodanie do innych doskonałych postów.

Czasami nie chcesz określać liczby argumentów i chcesz użyć do nich kluczy (kompilator będzie narzekał, jeśli jeden argument przekazany w słowniku nie zostanie użyty w metodzie).

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

Wtedy możesz robić takie rzeczy jak

myfuns = [manyArgs1, manyArgs2]
for fun in myfuns:
  fun(args)

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'})
____________

powyższy kod wyświetli

None None None
20 None 30
20 red 30

Jest to tak dobre, jak przekazywanie zmiennych za pomocą słownika


3
To okropny kod. O wiele lepiej byłoby:f = lambda **dic: ' '.join(dic.get(key, 'None') for key in 'abc')
metapertura

0

Inny sposób na obejście tego problemu, oprócz wspomnianych już miłych odpowiedzi, zależy od tego, że możesz przekazywać opcjonalne argumenty o nazwach według pozycji. Na przykład,

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

Wydajność

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

In [12]: f(1)
1
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.