Chame uma função com lista de argumentos em python

150

Estou tentando chamar uma função dentro de outra função em python, mas não consigo encontrar a sintaxe correta. O que eu quero fazer é algo como isto:

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

Nesse caso, a primeira chamada funcionará e a segunda não. O que eu quero modificar é a função de wrapper e não as funções chamadas.

SurDin
fonte

Respostas:

268

Para expandir um pouco as outras respostas:

Na linha:

def wrapper(func, *args):

O * próximo a argssignifica "pegue o restante dos parâmetros fornecidos e coloque-os em uma lista chamada args".

Na linha:

    func(*args)

O * próximo a argsaqui significa "pegue esta lista chamada args e a desembrulhe" nos demais parâmetros.

Então você pode fazer o seguinte:

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

Em wrapper2, a lista é passada explicitamente, mas nos dois wrappers argscontém a lista [1,2,3].

itsadok
fonte
26
Uma coisa que eu não encontrei mencionada com muita frequência é como chamar uma função com * args, se você tiver uma lista ou tupla que deseja passar. Para isso você precisa chamá-lo assim: wrapper1 (func2, * mylist)
Ali
* args in def wrapper(func, *args)é o que method(params object[] args)está em c #.
Jim Aho 04/03
1
Observe que *argsdeve ser o último argumento na definição da função.
Jim Aho
2
The * next to args means "take the rest of the parameters given and put them in a list called args".Ele os coloca em uma tupla, não em uma lista.
Tomasz Nocoń 4/11
20

A maneira mais simples de quebrar uma função

    func(*args, **kwargs)

... é escrever manualmente um wrapper que chamaria func () dentro de si:

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

No Python, a função é um objeto, para que você possa passar o nome como argumento de outra função e retorná-lo. Você também pode escrever um gerador de wrapper para qualquer função anyFunc () :

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

Observe também que no Python, quando você não sabe ou não deseja nomear todos os argumentos de uma função, pode se referir a uma tupla de argumentos, que é indicada por seu nome, precedida por um asterisco entre parênteses após o nome da função:

    *args

Por exemplo, você pode definir uma função que aceite qualquer número de argumentos:

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

O Python fornece ainda mais manipulação nos argumentos das funções. Você pode permitir que uma função aceite argumentos de palavras-chave. Dentro do corpo da função, os argumentos da palavra-chave são mantidos em um dicionário. Entre parênteses após o nome da função, este dicionário é indicado por dois asteriscos seguidos pelo nome do dicionário:

    **kwargs

Um exemplo semelhante que imprime o dicionário de argumentos de palavras-chave:

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

Você pode usar a sintaxe * args e ** kwargs para argumentos de comprimento variável.

O que significam * args e ** kwargs?

E do tutorial oficial do python

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

JimB
fonte
Então, eu preciso dele para obter * args e ** kwargs e chamá-lo com eles?
311 SurDin
1
Não, você pode usar um ou outro, mas eles geralmente são emparelhados. No seu caso, você só precisa de * args.
217 JimB
Ok, funciona, mas ainda não me deixa passar uma lista de argumentos, e eu tenho que passá-los separadamente. Não me incomoda muito, na minha situação atual, mas ainda assim seria bom saber como fazer isso. (Preciso fazer invólucro (func2, x, y, z) e não invólucro (func2, [x, y, z]))
Surdin
Se o último for o que você deseja, use o formulário * args quando o wrapper chamar func2, mas não no 'def wrapper'.
Alex Martelli #
10

A resposta literal à sua pergunta (para fazer exatamente o que você pediu, alterando apenas o invólucro, não as funções ou as chamadas de função) é simplesmente alterar a linha

func(args)

ler

func(*args)

Isso diz ao Python para pegar a lista fornecida (nesse caso args) e passar seu conteúdo para a função como argumentos posicionais.

Esse truque funciona nos dois "lados" da chamada de função, portanto, uma função definida assim:

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

seria capaz de aceitar o maior número possível de argumentos posicionais e colocá-los todos em uma lista chamada args.

Espero que isso ajude a esclarecer um pouco as coisas. Observe que isso também é possível com argumentos dicts / keyword, usando em **vez de *.

Alan Rowarth
fonte
8

Você precisa usar argumentos para descompactar ..

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
fonte
0

Uma pequena adição às respostas anteriores, já que não consegui encontrar uma solução para um problema, que não vale a pena abrir uma nova pergunta, mas me levou até aqui.

Aqui está um pequeno trecho de código, que combina lists, zip()e *args, para proporcionar um invólucro que pode lidar com uma quantidade desconhecida de funções com uma quantidade desconhecida de argumentos.

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)

Lembre-se de que zip()isso não fornece uma verificação de segurança para listas de tamanhos diferentes; consulte iteradores zip afirmando o mesmo comprimento em python .

P. Siehr
fonte