Rufen Sie eine Funktion mit Argumentliste in Python auf

150

Ich versuche, eine Funktion in einer anderen Funktion in Python aufzurufen, finde aber nicht die richtige Syntax. Was ich tun möchte, ist ungefähr so:

def wrapper(func, args):
    func(args)

def func1(x):
    print(x)

def func2(x, y, z):
    return x+y+z

wrapper(func1, [x])
wrapper(func2, [x, y, z])

In diesem Fall funktioniert der erste Anruf und der zweite nicht. Was ich ändern möchte, ist die Wrapper-Funktion und nicht die aufgerufenen Funktionen.

SurDin
quelle

Antworten:

268

Um die anderen Antworten ein wenig zu erweitern:

In der Schlange:

def wrapper(func, *args):

Das * neben argsbedeutet "Nehmen Sie den Rest der angegebenen Parameter und fügen Sie sie in eine Liste mit dem Namen args" ein.

In der Schlange:

    func(*args)

Das * neben argshier bedeutet "Nehmen Sie diese Liste mit dem Namen args und" entpacken "Sie sie in den Rest der Parameter.

Sie können also Folgendes tun:

def wrapper1(func, *args): # with star
    func(*args)

def wrapper2(func, args): # without star
    func(*args)

def func2(x, y, z):
    print x+y+z

wrapper1(func2, 1, 2, 3)
wrapper2(func2, [1, 2, 3])

In wrapper2wird die Liste explizit übergeben, aber in beiden Wrappern argsist die Liste enthalten [1,2,3].

itsadok
quelle
26
Eine Sache, die ich nicht sehr oft erwähnt habe, ist, wie man eine Funktion mit * args aufruft, wenn man eine Liste oder ein Tupel hat, die man übergeben möchte. Dafür müssen Sie es so nennen: wrapper1 (func2, * mylist)
Ali
* args in def wrapper(func, *args)ist das, was method(params object[] args)in C # steht.
Jim Aho
1
Beachten Sie, dass *argsdies das letzte Argument in der Funktionsdefinition sein muss.
Jim Aho
2
The * next to args means "take the rest of the parameters given and put them in a list called args".Es bringt sie in ein Tupel, nicht in eine Liste.
Tomasz Nocoń
20

Der einfachste Weg, eine Funktion zu verpacken

    func(*args, **kwargs)

... besteht darin, manuell einen Wrapper zu schreiben, der func () in sich selbst aufruft :

    def wrapper(*args, **kwargs):
        # do something before
        try:
            return func(*a, **kwargs)
        finally:
            # do something after

In Python ist die Funktion ein Objekt, sodass Sie den Namen als Argument einer anderen Funktion übergeben und zurückgeben können. Sie können auch einen Wrapper-Generator für jede Funktion anyFunc () schreiben :

    def wrapperGenerator(anyFunc, *args, **kwargs):
        def wrapper(*args, **kwargs):
            try:
                # do something before
                return anyFunc(*args, **kwargs)
            finally:
                #do something after
        return wrapper

Beachten Sie auch, dass Sie in Python, wenn Sie nicht alle Argumente einer Funktion kennen oder nicht benennen möchten, auf ein Tupel von Argumenten verweisen können, das durch seinen Namen gekennzeichnet ist, gefolgt von einem Sternchen in den Klammern danach der Funktionsname:

    *args

Sie können beispielsweise eine Funktion definieren, die eine beliebige Anzahl von Argumenten akzeptiert:

    def testFunc(*args):
        print args    # prints the tuple of arguments

Python bietet noch weitere Manipulationen an Funktionsargumenten. Sie können einer Funktion erlauben, Schlüsselwortargumente anzunehmen. Innerhalb des Funktionskörpers werden die Schlüsselwortargumente in einem Wörterbuch gespeichert. In den Klammern nach dem Funktionsnamen wird dieses Wörterbuch durch zwei Sternchen gefolgt vom Namen des Wörterbuchs gekennzeichnet:

    **kwargs

Ein ähnliches Beispiel, das das Schlüsselwortargument-Wörterbuch druckt:

    def testFunc(**kwargs):
        print kwargs    # prints the dictionary of keyword arguments
Alex
quelle
11

Sie können die Syntax * args und ** kwargs für Argumente variabler Länge verwenden.

Was bedeuten * args und ** kwargs?

Und aus dem offiziellen Python-Tutorial

http://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions

JimB
quelle
Also brauche ich es, um sowohl * args als auch ** kwargs zu bekommen und es mit ihnen zu nennen?
SurDin
1
Nein, Sie können entweder / oder verwenden, aber sie werden häufig miteinander gepaart. In Ihrem Fall benötigen Sie nur * args.
JimB
Ok, es funktioniert, aber es lässt mich immer noch keine Liste von Argumenten passieren, und ich muss sie separat weitergeben. Es stört mich in meiner gegenwärtigen Situation nicht sehr, aber es wäre trotzdem gut zu wissen, wie man das macht. (Ich muss Wrapper (func2, x, y, z) und nicht Wrapper (func2, [x, y, z]) machen)
SurDin
Wenn letzteres gewünscht wird, verwenden Sie das Formular * args, wenn der Wrapper func2 aufruft, jedoch nicht in 'def wrapper'.
Alex Martelli
10

Die wörtliche Antwort auf Ihre Frage (um genau das zu tun, was Sie gefragt haben, ändern Sie nur den Wrapper, nicht die Funktionen oder die Funktionsaufrufe) besteht einfach darin, die Zeile zu ändern

func(args)

lesen

func(*args)

Dies weist Python an, die angegebene Liste (in diesem Fall args) zu übernehmen und ihren Inhalt als Positionsargumente an die Funktion zu übergeben.

Dieser Trick funktioniert auf beiden "Seiten" des Funktionsaufrufs, sodass eine Funktion wie folgt definiert ist:

def func2(*args):
    return sum(args)

wäre in der Lage, so viele Positionsargumente zu akzeptieren, wie Sie darauf werfen, und sie alle in eine Liste mit dem Namen zu setzen args.

Ich hoffe, das hilft, die Dinge ein wenig zu klären. Beachten Sie, dass dies alles auch mit dicts / keyword-Argumenten möglich ist, indem Sie **anstelle von verwenden *.

Alan Rowarth
quelle
8

Sie müssen Argumente zum Entpacken verwenden.

def wrapper(func, *args):
    func(*args)

def func1(x):
    print(x)

def func2(x, y, z):
    print x+y+z

wrapper(func1, 1)
wrapper(func2, 1, 2, 3)
Joril
quelle
0

Eine kleine Ergänzung zu früheren Antworten, da ich keine Lösung für ein Problem finden konnte, die es nicht wert ist, eine neue Frage zu stellen, sondern mich hierher geführt hat.

Hier ist ein kleiner Code - Schnipsel, die kombiniert lists, zip()und *argseine Umhüllung zu schaffen , die mit einer unbekannten Menge von Funktionen mit einer unbekannten Menge von Argumenten umgehen kann.

def f1(var1, var2, var3):
    print(var1+var2+var3)

def f2(var1, var2):
    print(var1*var2)

def f3():
    print('f3, empty')

def wrapper(a,b, func_list, arg_list):
    print(a)
    for f,var in zip(func_list,arg_list):
        f(*var)
    print(b)

f_list = [f1, f2, f3]
a_list = [[1,2,3], [4,5], []]

wrapper('begin', 'end', f_list, a_list)

Beachten Sie, dass zip()dies keine Sicherheitsüberprüfung für Listen mit ungleicher Länge bietet. Siehe Zip-Iteratoren, die für die gleiche Länge in Python angeben .

P. Siehr
quelle