2010-05-26 24 views
19

¿Es posible que una función lambda tenga un número variable de argumentos? Por ejemplo, quiero escribir una metaclase, que crea un método para cada método de alguna otra clase y este método creado recientemente devuelve el valor opuesto al método original y tiene el mismo número de argumentos. Y quiero hacer esto con la función lambda. ¿Cómo pasar los argumentos? ¿Es posible?varargs en funciones lambda en Python

class Negate(type): 
    def __new__(mcs, name, bases, _dict): 
     extended_dict = _dict.copy() 
     for (k, v) in _dict.items(): 
      if hasattr(v, '__call__'): 
       extended_dict["not_" + k] = lambda s, *args, **kw: not v(s, *args, **kw) 
     return type.__new__(mcs, name, bases, extended_dict) 

class P(metaclass=Negate): 
    def __init__(self, a): 
     self.a = a 

    def yes(self): 
     return True 

    def maybe(self, you_can_chose): 
     return you_can_chose 

pero el resultado es totalmente erróneo:

>>>p = P(0) 
>>>p.yes() 
True 
>>>p.not_yes()  # should be False 
Traceback (most recent call last): 
    File "<pyshell#150>", line 1, in <module> 
    p.not_yes() 
    File "C:\Users\Desktop\p.py", line 51, in <lambda> 
    extended_dict["not_" + k] = lambda s, *args, **kw: not v(s, *args, **kw) 
TypeError: __init__() takes exactly 2 positional arguments (1 given) 
>>>p.maybe(True) 
True 
>>>p.not_maybe(True)  #should be False 
True 

Respuesta

27

No hay ningún problema usando varargs en funciones lambda. El problema aquí es diferente:

El problema es que el lambda refrences la variable de bucle v. Pero en el momento en que se llama a la lambda, el valor de v ha cambiado y la lambda llama a la función incorrecta. Esto siempre es algo de lo que hay que tener cuidado cuando defines un lambda en un bucle.

Puede solucionar este problema mediante la creación de una función adicional que contendrá el valor de v en un cierre:

def create_not_function(v): 
    return lambda s, *args, **kw: not v(s, *args, **kw) 

for (k, v) in _dict.items(): 
    if hasattr(v, '__call__'): 
     extended_dict["not_" + k] = create_not_function(v) 
+0

Enlace relacionado: http://code.activestate.com/recipes/502271/ –

10

Sí.

>>> l = lambda *x: print(x) 
>>> l(1,2,3) 
(1, 2, 3) 
+1

Tenga en cuenta que esto sólo funcionará con python3. Esta sintaxis no es compatible con Python2. – wohlgejm

+2

Más precisamente, 'lambda * x:' funcionará en Python 2, pero 'print' dentro de una lambda solo funciona en Python 3. – kennysong

+0

Tenga en cuenta que en' print (x) ', imprimirá los argumentos como una tupla, mientras 'print (* x)' llamaría print con los argumentos desempaquetados. –