Llamar a una función con lista de argumentos en python

150

Estoy tratando de llamar a una función dentro de otra función en python, pero no puedo encontrar la sintaxis correcta. Lo que quiero hacer es algo como esto:

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

En este caso, la primera llamada funcionará y la segunda no. Lo que quiero modificar es la función de contenedor y no las funciones llamadas.

SurDin
fuente

Respuestas:

268

Para ampliar un poco las otras respuestas:

En la linea:

def wrapper(func, *args):

El * al lado de argssignifica "tomar el resto de los parámetros dados y ponerlos en una lista llamada args".

En la linea:

    func(*args)

El * al lado de argsaquí significa "tomar esta lista llamada args y 'desenvolverla' en el resto de los parámetros.

Entonces puedes hacer lo siguiente:

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

En wrapper2, la lista se pasa explícitamente, pero en ambos contenedores argscontiene la lista [1,2,3].

itsadok
fuente
26
Una cosa que no encontré mencionada muy a menudo es cómo llamar a una función con * args, si tiene una lista o tupla que desea pasar. Para eso debes llamarlo así: wrapper1 (func2, * mylist)
Ali
* args in def wrapper(func, *args)es lo que method(params object[] args)está en C #.
Jim Aho
1
Tenga en cuenta que *argsdebe ser el último argumento en la definición de la función.
Jim Aho
2
The * next to args means "take the rest of the parameters given and put them in a list called args".Los pone en una tupla, no en una lista.
Tomasz Nocoń
20

La forma más sencilla de ajustar una función.

    func(*args, **kwargs)

... es escribir manualmente un contenedor que llame a func () dentro de sí mismo:

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

En Python, la función es un objeto, por lo que puede pasar su nombre como argumento de otra función y devolverlo. También puede escribir un generador de contenedor para cualquier función anyFunc () :

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

Tenga en cuenta también que en Python cuando no sabe o no quiere nombrar todos los argumentos de una función, puede referirse a una tupla de argumentos, que se denota por su nombre, precedida por un asterisco entre paréntesis después el nombre de la función:

    *args

Por ejemplo, puede definir una función que tome cualquier número de argumentos:

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

Python proporciona una manipulación aún mayor en los argumentos de la función. Puede permitir que una función tome argumentos de palabras clave. Dentro del cuerpo de la función, los argumentos de las palabras clave se encuentran en un diccionario. Entre paréntesis después del nombre de la función, este diccionario se denota con dos asteriscos seguidos del nombre del diccionario:

    **kwargs

Un ejemplo similar que imprime el diccionario de argumentos de palabras clave:

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

Puede usar la sintaxis * args y ** kwargs para argumentos de longitud variable.

¿Qué significan * args y ** kwargs?

Y del tutorial oficial de Python

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

JimB
fuente
Entonces, ¿lo necesito para obtener * args y ** kwargs y llamarlo con ellos?
SurDin
1
No, puede usar uno u otro, pero a menudo se combinan juntos. En su caso, solo necesita * args.
JimB
Ok, funciona, pero todavía no me deja pasar una lista de argumentos, y tengo que pasarlos por separado. No me molesta mucho, en mi situación actual, pero aún así sería bueno saber cómo hacer esto. (Necesito hacer wrapper (func2, x, y, z) y no wrapper (func2, [x, y, z]))
SurDin
Si esto último es lo que desea, use el formulario * args cuando el contenedor llama a func2, pero no en 'def wrapper'.
Alex Martelli
10

La respuesta literal a su pregunta (para hacer exactamente lo que le pidió, cambiando solo el contenedor, no las funciones o las llamadas a funciones) es simplemente alterar la línea

func(args)

leer

func(*args)

Esto le dice a Python que tome la lista dada (en este caso, args ) y pase su contenido a la función como argumentos posicionales.

Este truco funciona en ambos "lados" de la llamada a la función, por lo que una función definida como esta:

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

sería capaz de aceptar tantos argumentos posicionales como le arrojes, y colocarlos a todos en una lista llamada args.

Espero que esto ayude a aclarar un poco las cosas. Tenga en cuenta que todo esto también es posible con los dictados / argumentos de palabras clave, utilizando en **lugar de *.

Alan Rowarth
fuente
8

Necesita usar argumentos para desempaquetar.

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

Una pequeña adición a las respuestas anteriores, ya que no pude encontrar una solución para un problema, que no vale la pena abrir una nueva pregunta, pero me llevó aquí.

He aquí un pequeño fragmento de código, que combina lists, zip()y *args, para proporcionar un envoltorio que puede hacer frente a una cantidad desconocida de funciones con una cantidad desconocida 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)

Tenga en cuenta que eso zip()no proporciona una verificación de seguridad para las listas de longitud desigual, vea los iteradores de zip que afirman la misma longitud en python .

P. Siehr
fuente