I've tried to define a function that adds several lambda functions together.
When I call the function the kernel crashes and restarts with no further error messages.
def add_lambdas(function_list):
f = lambda x, y: None
for function in function_list:
f = lambda x, y: f(x, y) + function(x, y)
return f
f1 = lambda x, y: x + y
f2 = lambda x, y: x**2 - 2*y
f3 = lambda x, y: f1(x,y) + f2(x,y)
f4 = add_lambdas([f1,f2])
For example, when I call f3(2,3) all is well, but when I call f4(2,3) the kernel crashes and restarts. For f3 I do the adding of f1 and f2 manually, but for f4 I pass them through the function add_lambdas().
This is the output:
In[5]: f3(2,3)
Out[5]: 3
In[6]: f4(2,3)
Restarting kernel...
Obviously something is wrong with my defined function, but I cant figure out what it is. Any thoughts? Thank you!
I believe the problem is that you're defining f as a recursive function, which I don't think is what you want. You can try this instead:
def add_lambdas(function_list):
return lambda x, y: sum(f(x, y) for f in function_list)
You are getting a stack overflow. The problem is that there is only a single variable f and a single variable function in add_lambdas. When you write:
f = lambda x, y: f(x, y) + function(x, y)
you are just assigning a new value to the variable f, but the variable f inside the definition is also that new value. This is the stack overflow. In addition, the variable function will be left with the last value in the list. There is no reason for it to copy the value.
If instead you write:
f = lambda x, y, f=f, function=function: f(x, y) + function(x, y),
you're telling Python to assign f and function to new variables, (with the same name) and to use those new variables inside the lambda. Changes made to the other variables are immaterial
You'll discover a different error in your code. The first line needs to be
f = lambda x, y: 0 since otherwise you're adding an integer to None.
Note that #josemz is the simpler solution if you're just planning on adding things. If you want to learn to use lambdas within other lambdas, you need to make sure variables are bound correctly.
I was looking for an algorithm capable of storing and running functions with a flexible number of arguments. I ended up finding a switch/case analogue for python which meets my requirements:
def opA_2(x, y):
return x - y
def opB_3(x, y, z):
return x + y - z
def opC_2(x, y):
return x * y
def opD_3(x, y, z):
return x * y + z
op_dict = {'opA_2': opA_2,
'opB_3': opB_3,
'opC_2': opC_2,
'opD_3': opD_3
}
op_lambda_dict = {'opA_2': lambda x, y, kwargs: op_dict['opA_2'](x, y),
'opB_3': lambda x, y, kwargs: op_dict['opB_3'](x, y, kwargs['z']),
'opC_2': lambda x, y, kwargs: op_dict['opC_2'](x, y),
'opD_3': lambda x, y, kwargs: op_dict['opD_3'](x, y, kwargs['z']),
}
def dispatch_op(func_dict, op, x, y, **kwargs):
return func_dict.get(op, lambda a, b, c: None)(x, y, kwargs)
coefs_dict = {'i': 1, 'j': 2, 'k': 3, 'z': 4}
print('Original lambda dict result:', dispatch_op(op_lambda_dict, 'opB_3', 1, 2, **coefs_dict))
Resulting in:
Original lambda dict result: -1
Once I implemented this structure to my target code, however, I encountered many issues because my operations are defined via a loop.
As far as I understand it, this is because the lambda functions are not initialised, and they end up pointing to the last operation declared.
This additional code reproduces the issue:
op_looplambda_dict = {}
for label, func in op_dict.items():
if '2' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: func(x, y)
if '3' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: func(x, y, kwargs['z'])
print('Loop lambda dict result:', dispatch_op(op_looplambda_dict, 'opB_3', 1, 2, **coefs_dict))
Resulting in:
Loop lambda dict result: 6
This is the result of opD_3 instead of opB_3
I wonder if anyone could please offer any advice, on how to properly declare the lambda functions in the second case or a different code structure to avoid it. Thanks a lot.
The problem is to do with scope: each lambda function creates a "closure" over the func variable, and can access that variable's current value when the function runs. And because it's not running until the loop is complete, the value it uses is the last one it held in the loop.
The solution is therefore to utilise scope to your advantage, by ensuring that each lambda closes over a func variable which only ever holds one value - the value you need. You need a new scope for each iteration of the loop. And the only way Python allows you to create scope is with a function.
So the solution will look something like this:
op_looplambda_dict = {}
for label, func in op_dict.items():
def add_to_dict(function):
if '2' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: function(x, y)
if '3' in label:
op_looplambda_dict[label] = lambda x, y, kwargs: function(x, y, kwargs['z'])
add_to_dict(func)
I'd be the first to admit that this is a little clumsy, but it should work. The only difference from your code is that I've put the loop body inside a function - then immediately called that function each time. So there's no difference in how it behaves, except for the how the variables are scoped - which is key here. When those lambda function run, they will use the value of function from their immediately enclosing scope, and because off the add_to_dict function having its own scope, that will be a different function for each time through the loop.
I would encourage you to look at this Q&A for useful background - it's about Javascript rather than Python, but the scoping mechanisms (at least in "old-school" Javascript before ES6) are identical between the two languages, and the underlying issue you have here is identical to the one in this much-asked JS question. Unfortunately many of the solutions that are available in modern Javascript aren't applicable to Python, but this one does - a simple adaptation of the "Immediately Invoked Function Expression" (IIFE for short) pattern that's common in JS.
And as you can probably tell, I'm personally more experienced with JS than with Python, so I'd be interested to hear if there's a nicer, more idiomatic, Python solution to this problem than what I've done above.
The functions can be added in the dictionary using their names.
When you dispatch a call to an operation, you could compare the received arguments with signature of matched function in the registry.
This way you could figure out other arguments for that function from the optional arguments passed in the dispatch call. For example,
import inspect
import functools
registry = {}
def register_operation(func):
if func.__name__ in registry:
raise TypeError("duplicate registration")
func.__signature = inspect.signature(func)
registry[func.__name__] = func
return func
def dispatch_operation(func_name, *args, **kwargs):
func = registry.get(func_name, None)
if not func:
raise TypeError("no match")
rest_parameters = list(func.__signature.parameters)[len(args):]
rest_args = (kwargs.get(p) for p in rest_parameters)
ba = func.__signature.bind(*args, *rest_args)
ba.apply_defaults()
return func(*ba.args, **ba.kwargs)
#register_operation
def opA_2(x, y):
return x - y
#register_operation
def opB_3(x, y, z):
return x + y - z
#register_operation
def opC_2(x, y):
return x * y
#register_operation
def opD_3(x, y, z):
return x * y + z
coefs_dict = {'i': 1, 'j': 2, 'k': 3, 'z': 4}
print('Original dict result:', dispatch_operation('opB_3', 1, 2, **coefs_dict))
I'm wondering why these 2 pieces of code are different?
This code gives me the answer of 5
curry2 = lambda f: lambda x: lambda y: f(x, y)
m = curry2(add)
m(2)(3)
5
This one gives me the location of my function
def lambda_curry2(func):
return lambda f: lambda x: lambda y: f(x, y)
curry2 = lambda_curry2(add)
add_three = curry2(3)
add_three(5)
The second one isn't using func. You don't need as many lambdas, because func is the function you want to call.
So it should be;
def lambda_curry2(func):
return lambda x: lambda y: func(x, y)
Put another way, your definition of curry2 is equivalent to:
def curry2(f):
return lambda x: lambda y: f(x, y)
In general,
name = lambda <vars>: <expression>
is short for
def name(<vars>):
return <expression>
lambda is usually used when you don't need to name the function (e.g. when you want to pass a simple function as an argument or return it as a value).
If I state a lambda function like this:
someLambdaFunction = lambda x,y: x+x**2+y
can I reverse the ordering of that lambda to say
someOtherLambdaFunction = lambda y,x: x+x**2+y
without rewriting the whole lambda function? Just switch the arguments?
Yes, you can do something like this:
someLambdaFunction = lambda x,y: x+x**2+y
someOtherLambdaFunction = lambda y, x: someLambdaFunction(x, y)
Basically, reuse the original someLambdaFunction
You can write a general function that swaps the first two arguments of another function:
def swap2(func):
""" Swap the first two arguments of a function """
def wrapped(x, y, *args, **kwargs):
return func(y, x, *args, **kwargs)
return wrapped
f = lambda x,y: x+x**2+y
f_swapped= swap2(f)
assert f(3, 4) == f_swapped(4, 3)
This works because functions are just regular Python objects, that can be passed around just like integers, strings, etc.
By the way, there is almost never a reason to write something = lambda .... Use def something(...) instead.
I want to pass a Python function to another function with some of its parameters "filled out" ahead of time.
This is simplification what I am doing:
def add(x, y):
return x + y
def increment_factory(i): # create a function that increments by i
return (lambda y: add(i, y))
inc2 = increment_factory(2)
print inc2(3) # prints 5
I don't want to use some sort of passing of args and later exploding it with *args because the function I am passing inc2 into doesn't know to pass args to it.
This feels a bit too clever for a group project... is there a more straightforward or pythonic way to do this?
Thanks!
This is called currying, or partial application. You can use the built-in functools.partial(). Something like the following would do what you want.
import functools
def add(x,y):
return x + y
inc2 = functools.partial(add, 2)
print inc2(3)
You could also accomplish the same with a lambda function:
inc2 = lambda y: add(2, y)
print inc2(3)