Chiama una funzione con un elenco di argomenti in Python


150

Sto provando a chiamare una funzione all'interno di un'altra funzione in Python, ma non riesco a trovare la sintassi corretta. Quello che voglio fare è qualcosa del genere:

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 questo caso la prima chiamata funzionerà e la seconda no. Quello che voglio modificare è la funzione wrapper e non le funzioni chiamate.

Risposte:


268

Per espandere un po 'le altre risposte:

Nella linea:

def wrapper(func, *args):

* Accanto a argssignifica "prendi il resto dei parametri dati e inseriscili in un elenco chiamato args".

Nella linea:

    func(*args)

* Accanto a argsquesto significa "prendere questo elenco chiamato args e" scartarlo "nel resto dei parametri.

Quindi puoi fare quanto segue:

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 wrapper2, l'elenco viene passato esplicitamente, ma in entrambi i wrapper argscontiene l'elenco [1,2,3].


26
Una cosa che non ho trovato menzionato molto spesso è come chiamare una funzione con * args, se hai un elenco o una tupla che vuoi passare. Per questo è necessario chiamarlo così: wrapper1 (func2, * mylist)
Ali

* args in def wrapper(func, *args)è ciò che method(params object[] args)è in C #.
Jim Aho,

1
Si noti che *argsdeve essere l'ultimo argomento nella definizione della funzione.
Jim Aho,

2
The * next to args means "take the rest of the parameters given and put them in a list called args".Li mette in una tupla, non in un elenco.
Tomasz Nocoń,

20

Il modo più semplice per avvolgere una funzione

    func(*args, **kwargs)

... è scrivere manualmente un wrapper che chiamerebbe func () al suo interno:

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

In Python la funzione è un oggetto, quindi puoi passare il suo nome come argomento di un'altra funzione e restituirlo. Puoi anche scrivere un generatore di wrapper per qualsiasi funzione anyFunc () :

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

Si noti inoltre che in Python quando non si conoscono o non si desidera nominare tutti gli argomenti di una funzione, è possibile fare riferimento a una tupla di argomenti, che è indicata dal suo nome, preceduta da un asterisco tra parentesi dopo il nome della funzione:

    *args

Ad esempio, puoi definire una funzione che accetta qualsiasi numero di argomenti:

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

Python prevede un'ulteriore manipolazione degli argomenti delle funzioni. È possibile consentire a una funzione di accettare argomenti per parole chiave. All'interno del corpo della funzione gli argomenti delle parole chiave sono contenuti in un dizionario. Tra parentesi dopo il nome della funzione questo dizionario è indicato da due asterischi seguiti dal nome del dizionario:

    **kwargs

Un esempio simile che stampa il dizionario degli argomenti delle parole chiave:

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

11

Puoi usare la sintassi * args e ** kwargs per argomenti a lunghezza variabile.

Cosa significano * args e ** kwargs?

E dal tutorial ufficiale di Python

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


Quindi ne ho bisogno per ottenere sia * args che ** kwargs e chiamarlo con loro?
SurDin,

1
No, puoi usare / o, ma sono spesso accoppiati insieme. Nel tuo caso, hai solo bisogno di * args.
JimB,

Ok, funziona, ma non mi consente ancora di passare un elenco di argomenti e devo passarli separatamente. Non mi dà molto fastidio, nella mia situazione attuale, ma sarebbe comunque bello sapere come farlo. (Ho bisogno di fare wrapper (func2, x, y, z) e non wrapper (func2, [x, y, z]))
SurDin

Se quest'ultimo è quello che vuoi, usa il modulo * args quando wrapper chiama func2, ma non in 'def wrapper'.
Alex Martelli,

10

La risposta letterale alla tua domanda (per fare esattamente quello che hai chiesto, cambiando solo il wrapper, non le funzioni o le chiamate di funzione) è semplicemente quella di modificare la linea

func(args)

leggere

func(*args)

Questo dice a Python di prendere la lista fornita (in questo caso args) e di passarne il contenuto alla funzione come argomenti posizionali.

Questo trucco funziona su entrambi i "lati" della chiamata di funzione, quindi una funzione definita in questo modo:

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

sarebbe in grado di accettare tutti gli argomenti posizionali che ci si lancia e inserirli tutti in un elenco chiamato args.

Spero che questo aiuti a chiarire un po 'le cose. Nota che questo è possibile anche con argomenti dicts / keyword, usando **invece di *.


8

È necessario utilizzare gli argomenti per decomprimere ..

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)

0

Una piccola aggiunta alle risposte precedenti, dal momento che non riuscivo a trovare una soluzione a un problema, che non vale la pena aprire una nuova domanda, ma mi ha portato qui.

Ecco un piccolo frammento di codice, che unisce lists, zip()e *args, per fornire un involucro in grado di affrontare una quantità sconosciuta di funzioni con una quantità sconosciuta di argomenti.

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)

Tieni presente che zip()non fornisce un controllo di sicurezza per elenchi di lunghezza diversa, vedi gli iteratori zip che affermano che Python ha uguale lunghezza .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.