Wywołaj funkcję z listą argumentów w pythonie


150

Próbuję wywołać funkcję wewnątrz innej funkcji w Pythonie, ale nie mogę znaleźć odpowiedniej składni. To, co chcę zrobić, to coś takiego:

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])

W takim przypadku pierwsze połączenie zadziała, a drugie nie. To, co chcę zmodyfikować, to funkcja opakowania, a nie wywoływane funkcje.

Odpowiedzi:


268

Aby rozwinąć trochę inne odpowiedzi:

W wierszu:

def wrapper(func, *args):

Znak * obok argsoznacza „weź resztę podanych parametrów i umieść je na liście o nazwie args”.

W wierszu:

    func(*args)

Znak * obok argstutaj oznacza „weź tę listę o nazwie args i„ rozpakuj ”ją do pozostałych parametrów.

Możesz więc wykonać następujące czynności:

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])

W wrapper2programie lista jest przekazywana jawnie, ale w obu opakowaniach argslista zawiera [1,2,3].


26
Jedną rzeczą, o której nie wspomniałem zbyt często, jest wywoływanie funkcji z * args, jeśli masz listę lub krotkę, którą chcesz przekazać. W tym celu musisz to nazwać w ten sposób: wrapper1 (func2, * mylist)
Ali

* args w def wrapper(func, *args)jest tym, co method(params object[] args)jest w C #.
Jim Aho

1
Zauważ, że *argsmusi to być ostatni argument w definicji funkcji.
Jim Aho

2
The * next to args means "take the rest of the parameters given and put them in a list called args".Umieszcza je w krotce, a nie na liście.
Tomasz Nocoń

20

Najprostszy sposób na zawijanie funkcji

    func(*args, **kwargs)

... polega na ręcznym napisaniu opakowania, które wywoływałoby func () wewnątrz siebie:

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

W Pythonie funkcja jest obiektem, więc możesz przekazać jej nazwę jako argument innej funkcji i zwrócić ją. Możesz także napisać generator opakowania dla dowolnej funkcji anyFunc () :

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

Zwróć również uwagę, że w Pythonie, gdy nie znasz lub nie chcesz nazywać wszystkich argumentów funkcji, możesz odwołać się do krotki argumentów, która jest oznaczona nazwą, poprzedzoną gwiazdką w nawiasach po nazwa funkcji:

    *args

Na przykład możesz zdefiniować funkcję, która pobierze dowolną liczbę argumentów:

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

Python umożliwia jeszcze dalsze manipulowanie argumentami funkcji. Możesz zezwolić funkcji na przyjmowanie argumentów słów kluczowych. W treści funkcji argumenty słów kluczowych są przechowywane w słowniku. W nawiasach po nazwie funkcji słownik ten jest oznaczony dwiema gwiazdkami, po których następuje nazwa słownika:

    **kwargs

Podobny przykład, który wyświetla słownik argumentów słów kluczowych:

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

11

Możesz użyć składni * args i ** kwargs dla argumentów o zmiennej długości.

Co oznaczają * args i ** kwargs?

I z oficjalnego samouczka Pythona

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


Więc potrzebuję go, aby uzyskać zarówno * args, jak i ** kwargs i wywołać je z nimi?
SurDin

1
Nie, możesz użyć opcji albo / albo, ale często są one połączone razem. W twoim przypadku potrzebujesz tylko * args.
JimB

Ok, działa, ale nadal nie pozwala mi przekazać listy argumentów i muszę je przekazać osobno. Nie przeszkadza mi to zbytnio w mojej obecnej sytuacji, ale nadal dobrze byłoby wiedzieć, jak to zrobić. (Muszę zrobić wrapper (func2, x, y, z), a nie wrapper (func2, [x, y, z]))
SurDin

Jeśli chcesz, użyj formularza * args, gdy wrapper wywołuje func2, ale nie w 'wrapper def'.
Alex Martelli

10

Dosłowną odpowiedzią na twoje pytanie (aby zrobić dokładnie to, o co prosiłeś, zmieniając tylko opakowanie, a nie funkcje lub wywołania funkcji) jest po prostu zmiana wiersza

func(args)

czytać

func(*args)

Mówi to Pythonowi, aby wziął podaną listę (w tym przypadku args) i przekazał jej zawartość do funkcji jako argumenty pozycyjne.

Ta sztuczka działa po obu „stronach” wywołania funkcji, więc funkcja zdefiniowana w ten sposób:

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

byłby w stanie zaakceptować tyle argumentów pozycyjnych, ile do niego rzucisz, i umieścić je wszystkie na liście o nazwie args.

Mam nadzieję, że pomoże to trochę wyjaśnić sprawy. Zauważ, że to wszystko jest również możliwe w przypadku argumentów dicts / słów kluczowych, używając **zamiast *.


8

Musisz rozpakować argumenty.

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

Mały dodatek do poprzednich odpowiedzi, ponieważ nie mogłem znaleźć rozwiązania problemu, którego nie warto otwierać nowego pytania, ale zaprowadził mnie tutaj.

Oto mały fragment kodu, który łączy lists, zip()a *args, aby zapewnić, że opakowanie może poradzić sobie z nieznaną ilością funkcji z nieznaną ilością argumentów.

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)

Należy pamiętać, że zip()nie zapewnia to kontroli bezpieczeństwa dla list o nierównej długości, zobacz iteratory zip zapewniające równą długość w Pythonie .

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.