2012-08-09 17 views
5
def f1(n): #accepts one argument 
    pass 

def f2(): #accepts no arguments 
    pass 

FUNCTION_LIST = [(f1,(2)), #each list entry is a tuple containing a function object and a tuple of arguments 
       (f1,(6)), 
       (f2,())] 

for f, arg in FUNCTION_LIST: 
    f(arg) 

La tercera ronda de tiempo en el ciclo, intenta pasar una tupla de argumentos vacía a una función que no acepta argumentos. Da el error TypeError: f2() takes no arguments (1 given). Las dos primeras llamadas funcionan correctamente: se pasa el contenido de la tupla, no la tupla misma.Python - lista de tuplas de función/argumento

Deshacerse de la tupla vacía de argumentos en la entrada de la lista infractor no resuelve el problema:

FUNCTION_LIST[2] = (f2,) 
for f,arg in FUNCTION_LIST: 
    f(arg) 

resultados en ValueError: need more than 1 value to unpack.

También he intentado iterar sobre el índice en lugar de los elementos de la lista.

for n in range(len(FUNCTION_LIST)): 
    FUNCTION_LIST[n][0](FUNCTION_LIST[n][1]) 

Esto le da al mismo TypeError en el primer caso, y IndexError: tuple index out of range cuando la tercera entrada de la lista es (f2,).

Finalmente, la notación de asterisco tampoco funciona. Esta vez los errores en la llamada a f1:

for f,args in FUNCTION_LIST: 
    f(*args) 

da TypeError: f1() argument after * must be a sequence, not int.

Me he quedado sin cosas que probar. Sigo pensando que el primero debería funcionar. ¿Alguien puede señalarme en la dirección correcta?

Respuesta

8

su comentario en este fragmento de código muestra un error muy relevantes en este contexto:

FUNCTION_LIST = [(f1,(2)), #each list entry is a tuple containing a function object and a tuple of arguments 
       (f1,(6)), 
       (f2,())] 

Las expresiones (2) y (6) no son tuplas - son números enteros. Debe usar (2,) y (6,) para indicar las tuplas de un solo elemento que desea. Después de arreglar esto, el código de bucle debe ser así:

for f, args in FUNCTION_LIST: 
    f(*args) 

Ver Unpacking Argument Lists en el tutorial de Python para una explicación de la sintaxis *args.

+0

Gracias por la gran respuesta.Pregunta del corolario: ¿es posible realizar operaciones sobre el valor de retorno de las funciones? Digamos que quería duplicar la primera entrada: 'FUNCTION_LIST = [(f1 * 2, (2)), (f1, (6)), (f2,())]' no funcionaría, lo haría, porque estarías tratando de aplicar el operador de multiplicación a una función _objeto_, no el valor devuelto por la función. –

+0

Relacionado: ¿es posible llamar a otras funciones en los argumentos tuple, que se evaluarían cuando se llame a la función? 'FUNCTION_LIST = [(f1, (time.time(),)), (f1, (6)), (f2,())]' no me daría algo dependiente del tiempo, porque la llamada al tiempo. time() se evaluaría cuando la lista se rellene. –

+1

@poorsod: para la ejecución retrasada, debe definir las funciones que hacen lo que quiere. Por ejemplo 'def double_f1 (x): return 2 * f1 (x)' o 'def f1_time(): return f1 (time.time)'. En algunos casos, puede salirse con la suya con las funciones 'lambda', pero yo recomendaría utilizarlo con moderación. –

0

Intente pasar *() en lugar de (). El símbolo * le dice a python que descomprima el iterable que lo sigue, por lo que desempaqueta la tupla vacía y no pasa nada a la función, ya que la tupla estaba vacía.

2

El problema es que esa notación:

(6) 

evalúa a un entero de valor y lo que necesita tupla, por lo que escribir de esta manera:

(6,) 

y su notación asterisco tendrá éxito.

0

Para el registro, una buena alternativa que he descubierto desde entonces es el uso de functools.partial. El siguiente código hace lo que estaba tratando de hacer:

from functools import partial 

def f1(n): #accepts one argument 
    pass 

def f2(): #accepts no arguments 
    pass 

FUNCTION_LIST = [partial(f1,2), #each list entry is a callable with the argument pre-ordained 
       partial(f1,6), 
       partial(f2)] #the call to partial is not really necessary for the entry with no arguments. 

for f in FUNCTION_LIST: f() 
Cuestiones relacionadas