Викличте функцію зі списком аргументів у 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].


26
Одна річ, яку я не знаходив дуже часто, це те, як викликати функцію * args, якщо у вас є список або кортеж, який ви хочете передати. Для цього потрібно назвати це так: wrapper1 (func2, * mylist)
Алі

* args in 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

11

Ви можете використовувати * args та ** kwargs синтаксис для аргументів змінної довжини.

Що означають * args та ** kwargs?

І з офіційного підручника пітона

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


Тож мені це потрібно, щоб отримати як * args, так і ** kwargs і викликати його разом з ними?
SurDin

1
Ні, ви можете використовувати або /, або, але вони часто спарюються разом. У вашому випадку вам потрібні лише * args.
JimB

Гаразд, це працює, але все ще не дає мені передати список аргументів, і мені доведеться передати їх окремо. Мене це не сильно турбує в моїй теперішній ситуації, але все-таки було б добре знати, як це зробити. (Мені потрібно робити обгортку (func2, x, y, z), а не обгортку (func2, [x, y, z]))
SurDin

Якщо останнє - те, що ви хочете, використовуйте форму * args, коли обгортка викликає func2, але не в оболонці 'def'.
Алекс Мартеллі

10

Буквальна відповідь на ваше запитання (робити саме те, що ви задали, змінюючи лише обгортку, а не функції або виклики функції) - це просто змінити рядок

func(args)

читати

func(*args)

Це говорить Python взяти даний список (в даному випадку args) і передати його вміст функції як позиційні аргументи.

Цей трюк працює з обох "сторін" виклику функції, тому функція визначена так:

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

зможе прийняти стільки позиційних аргументів, скільки ви кинете на це, і помістити їх у список, який називається args.

Я сподіваюся, що це допоможе трохи прояснити речі. Зауважте, що це все можливе і за допомогою аргументів дик / ключових слів, використовуючи **замість них *.


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)

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()не забезпечує перевірку безпеки для списків нерівної довжини, см блискавки ітератори затвердження для однакової довжини в пітона .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.