Вызов функции со списком аргументов в Python

150

Я пытаюсь вызвать функцию внутри другой функции в Python, но не могу найти правильный синтаксис. Что я хочу сделать, это что-то вроде этого:

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

В этом случае первый вызов будет работать, а второй - нет. Я хочу изменить функцию-обертку, а не вызываемые функции.

Сурдин
источник

Ответы:

268

Чтобы немного расширить другие ответы:

В линии:

def wrapper(func, *args):

Знак * рядом с argsозначает «взять остальные заданные параметры и поместить их в список с именем args».

В линии:

    func(*args)

Знак «рядом с этим» argsозначает «взять этот список, называемый args, и« развернуть »его в остальных параметрах.

Таким образом, вы можете сделать следующее:

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

В wrapper2, список передается явно, но в обеих оболочках argsсодержится список [1,2,3].

itsadok
источник
26
Одна вещь, которую я не часто упоминал, это то, как вызывать функцию с * args, если у вас есть список или кортеж, который вы хотите передать. Для этого вам нужно назвать это следующим образом: wrapper1 (func2, * mylist)
Али
Аргументы в def wrapper(func, *args)это то, что method(params object[] args)в C #.
Джим Ахо
1
Обратите внимание, что это *argsдолжен быть последний аргумент в определении функции.
Джим Ахо
2
The * next to args means "take the rest of the parameters given and put them in a list called args".Он помещает их в кортеж, а не в список.
Томаш Ноконь,
20

Самый простой способ обернуть функцию

    func(*args, **kwargs)

... состоит в том, чтобы вручную написать оболочку, которая будет вызывать func () внутри себя:

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

В Python функция является объектом, поэтому вы можете передать ее имя в качестве аргумента другой функции и вернуть его. Вы также можете написать генератор-обертку для любой функции anyFunc () :

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

Также обратите внимание, что в Python, когда вы не знаете или не хотите называть все аргументы функции, вы можете ссылаться на кортеж аргументов, который обозначается его именем, которому предшествует звездочка в скобках после имя функции:

    *args

Например, вы можете определить функцию, которая будет принимать любое количество аргументов:

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

Python предусматривает еще больше манипуляций с аргументами функций. Вы можете разрешить функции принимать ключевые аргументы. Внутри тела функции ключевые аргументы хранятся в словаре. В скобках после имени функции этот словарь обозначается двумя звездочками, за которыми следует название словаря:

    **kwargs

Аналогичный пример, который печатает словарь аргументов ключевого слова:

    def testFunc(**kwargs):
        print kwargs    # prints the dictionary of keyword arguments
Alex
источник
11

Вы можете использовать синтаксис * args и ** kwargs для аргументов переменной длины.

Что означают * args и ** kwargs?

И из официального учебника по питону

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

JimB
источник
Так что мне нужно, чтобы получить и * args и ** kwargs и вызвать его с ними?
SurDin
1
Нет, вы можете использовать либо / или, но они часто в паре вместе. В вашем случае вам нужны только * args.
ДжимБ
Хорошо, это работает, но все равно не позволяет мне передать список аргументов, и я должен передать их отдельно. В моей нынешней ситуации это меня не особо беспокоит, но все же было бы хорошо знать, как это сделать. (Мне нужно сделать оболочку (func2, x, y, z), а не оболочку (func2, [x, y, z]))
SurDin
Если последнее - то, что вам нужно, используйте форму * args, когда обертка вызывает func2, но не в 'def wrapper'.
Алекс Мартелли
10

Буквальный ответ на ваш вопрос (чтобы сделать именно то, что вы просили, изменив только обертку, а не функции или вызовы функций), просто изменить строку

func(args)

читать

func(*args)

Это говорит Python, чтобы взять данный список (в этом случае, args ) и передать его содержимое функции в качестве позиционных аргументов.

Этот прием работает с обеих сторон вызова функции, поэтому функция определяется следующим образом:

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

будет в состоянии принять столько позиционных аргументов, сколько вы бросите на него, и поместить их все в список с именем args.

Надеюсь, это поможет немного прояснить ситуацию. Обратите внимание, что это все возможно и с аргументами dicts / keyword, используя **вместо *.

Алан Роуарт
источник
8

Вам нужно использовать аргументы распаковки ..

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
источник
0

Небольшое дополнение к предыдущим ответам, так как я не смог найти решение проблемы, которая не стоит открывать новый вопрос, но привела меня сюда.

Вот небольшой фрагмент кода, который сочетает в себе lists, zip()и *args, чтобы обеспечить оболочку , которая может иметь дело с неизвестным количеством функций с неизвестным количеством аргументов.

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)

Имейте в виду, что zip()это не обеспечивает проверку безопасности для списков неравной длины, см. Zip-итераторы, утверждающие равную длину в python .

П. Сиер
источник