Is it possible a lambda function to have variable number of arguments? For example, I want to write a metaclass, which creates a method for every method of some other class and this newly created method returns the opposite value of the original method and has the same number of arguments. And I want to do this with lambda function. How to pass the arguments? Is it possible?
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
But the result is totally wrong:
>>>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
There is no problem using varargs in lambda functions. The issue here is different:
The problem is that the the lambda refrences the loop variable v
. But by the time the lambda is called, the value of v
has changed and the lambda calls the wrong function. This is always something to watch out for when you define a lambda in a loop.
You can fix this by creating an additional function which will hold the value of v
in a closure:
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)