Unexpected result when changing list of functions (lambda) [duplicate] - python

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 2 years ago.
I have a list of functions each of these takes a parameter.
I want to use a function of a library which takes functions but expects them to have no parameters.
So I want to create a list of new functions using lambda to pass the param "externally"
However the new list of functions doesn't yield the expected result.
(This is some minimal example)
def fun_a(param):
print("a")
def fun_b(param):
print("b")
def do_something(funs):
funs_out = []
for fun in funs:
funs_out.append(lambda: fun(0))
return funs_out
funs = [fun_a,fun_b]
funs[0](0) # prints a
funs[1](0) # prints b
funs_changed = do_something(funs)
#funs_changed = [lambda: f(0) for f in funs]
funs_changed[0]() # prints b ??? expected a
funs_changed[1]() # prints b
I tried funs_changed = [lambda: f(0) for f in funs] before as it seems more pythonic, and then tried to use more explicit code (raw for loop) to find the root cause but without success.
What do I miss?

You can use functools.partial:
from functools import partial
def fun_a(param):
print("a")
def fun_b(param):
print("b")
def do_something(funs):
funs_out = []
for fun in funs:
funs_out.append(partial(fun, 0))
return funs_out
funs = [fun_a, fun_b]
funs[0](0) # prints a
funs[1](0) # prints b
funs_changed = do_something(funs)
# funs_changed = [partial(fun, 0) for f in funs]
funs_changed[0]() # prints a
funs_changed[1]() # prints b
From this answer:
Roughly, partial does something like this (apart from keyword args
support etc):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper

just use value hack:
funs_changed = [lambda f=f: f(0) for f in funs]
it would be OK and produce an output of:
a
b
a
b
Also there is a solution for your initial variant of the code: just patch do_something function if you prefer keep it as function to avoid in-line lamda-list comprehention in main code contex (we'll do it in function):
def do_something(funs):
funs_out = [lambda f=f: f(0) for f in funs]
return funs_out

Related

At what moment are closure cells evaluated? Can lambas have closures? [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed last year.
Consider the following code:
def A():
l = list()
for i in range(5):
l.append(lambda: i)
return l
for f in A():
print( f() )
It prints out 4 five times, and I'm assuming it does that because the lambda function has taken the variable i through the outer scope of A, but right before the moment the variable became out of scope.
Then we have this code:
def B():
l = list()
for i in range(5):
l.append((lambda i: lambda: i)(i))
return l
for f in B():
print( f() )
It prints out all numbers from 0 to 4 in order. I am assuming it does that because the variable i has been grabbed from the scope of the outer lambda which took it as a parameter, so the value has been assigned to the cell in the moment when that external lambda finished executing.
But what if i held a mutable object instead of an immutable int?
def C():
l, i = list(), list()
for r in range(5):
i.append(r)
l.append((lambda i: lambda: i)(i.copy()))
return l
for f in C():
print( f() )
It does print out the lists in order as expected, because I used the list.copy() method in the argument.
Now, is my understanding correct?
If so, then is there a more pythonic or simpler way to save in a closure cell either an immutable object (or a copy of a mutable object), at the exact moment I want to? In other words, is there a better way than the (lambda i: lambda: i)(i) latch I came up with?
This happens because of late binding, try this code:
def bind(value):
return lambda: value
def A():
l = list()
for i in range(5):
l.append(bind(i))
return l
for f in A():
print( f() )
This way you can obtain the expected behaviour, since the binding is now on a value and not on a variable. Of course this is something tricky, and you should pay attemption to the behaviours of function like this, since python is using late binding.

What is the syntax for the input for a def function with multiple nested functions?

I'm learning Python right now and I am just trying to get to grips with all of the syntax options.
Currently, the only thing that I can't seem to google up is what to do if I for some reason want to define a function which contains multiple other defines.
While I understand what to do if there's only 1 define inside the the larger define (val = f()(3,4) returns 7 if you exclude the second def below), I don't know how to correctly use the function below.
If it's possible, what is the syntax for a def function with an arbitrary amount of defined functions within it?
Code:
def f():
def x(a,b):
return a + b
return x
def y(c,d):
return c + d
return y
val = f()(3,4)(5,6)
print(val)
I expected the above to return either (7,11) or 11. However, it returns 'int object is not callable'
When you write val = f()(3,4)(5,6), you want f to return a function that also returns a function; compare with the simpler multi-line call:
t1 = f()
t2 = t1(3,4)
val = t2(5,6)
The function f defines and returns also has to define and return a function that can be called with 2 arguments. So, as #jonrsharpe said, you need more nesting:
def f():
def x(a, b):
def y(c, d):
return c + d
return y
return x
Now, f() produces the function named x, and f()(3,4) produces the function named y (ignoring its arguments 3 and 4 in the process), and f()(3,4)(5,6) evaluates (ultimately) to 5 + 6.

How do I capture the CURRENT values of closure variables immutably in Python? [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
If I do
def f():
a = 1
g = lambda: a
a = 2
return g
print(f()())
The value that is printed is 2, because a is mutated after g is constructed.
How do I get g to capture the value of a statically so that later modifications are ignored?
For simple cases, such as when the code is short and we don't have many variables to capture, we can create a temporary lambda and call it:
def f():
a = 1
g = (lambda a: lambda: a)(a)
a = 2
return g
The issue here is that the code can quickly become harder to read.
Alternatively, we can capture the variable as an optional argument:
def f():
a = 1
g = lambda a=a: a
a = 2
return g
The issue here is, of course, that we might not want the caller to be able to specify this parameter.
(And the code can be a little less readable, too.)
A fully general solution might be the following, except that it does not capture globals:
def bind(*args, **kwargs):
# Use '*args' so that callers aren't limited in what names they can specify
func = args[0]
include_by_default = args[1] if len(args) > 1 else None
# if include_by_default == False, variables are NOT bound by default
if include_by_default == None: include_by_default = not kwargs
fc = func.__code__
fv = fc.co_freevars
q = func.__closure__
if q:
ql = []
i = 0
for qi in q:
fvi = fv[i]
ql.append((lambda v: (lambda: v).__closure__[0])(
kwargs.get(fvi, qi.cell_contents))
if include_by_default or fvi in kwargs
else qi)
i += 1
q = q.__class__(ql)
del ql
return func.__class__(fc, func.__globals__, func.__name__, func.__defaults__, q)
The reason I do not attempt to capture globals here is that the semantics can get confusing -- if an inner function says global x; x = 1, it certainly does want the global x to be modified, so suppressing this change would quickly make the code very counterintuitive.
However, barring that, we would be able to simply use it as follows:
def f():
a = 1
g = bind(lambda: a)
a = 2
return g
print(f()())
And voilĂ , a is instantly captured. And if we want to only capture some variables, we can do:
def f():
a = 1
b = 2
g = bind(lambda: a + b, b=5) # capture 'b' as 5; leave 'a' free
a = 2
b = 3
return g
print(f()())
In python3.8 the CellType class was added to types, which means you can create a function with a custom closure. That allows us to write a function that converts functions with closures that reference a parent frame to closures with static values:
from types import FunctionType, CellType
def capture(f):
""" Returns a copy of the given function with its closure values and globals shallow-copied """
closure = tuple(CellType(cell.cell_contents) for cell in f.__closure__)
return FunctionType(f.__code__, f.__globals__.copy(), f.__name__, f.__defaults__, closure)
print([f() for f in [ lambda: i for i in range(5)]]) # Outputs [4, 4, 4, 4, 4]
print([f() for f in [capture(lambda: i) for i in range(5)]]) # Outputs [0, 1, 2, 3, 4]
The capture function could be tweaked in a few ways; The current implementation captures all globals and free vars, and does so shallowly. You could decide to deepcopy the captured values, to capture only the free vars, to capture only specific variable names according to an argument, etc.

Python - Passing a function into another function [duplicate]

This question already has answers here:
Python function as a function argument?
(10 answers)
Closed last month.
I am solving a puzzle using python and depending on which puzzle I am solving I will have to use a special set of rules. How can I pass a function into another function in Python?
Example
def Game(listA, listB, rules):
if rules == True:
do...
else:
do...
def Rule1(v):
if "variable_name1" in v:
return False
elif "variable_name2" in v:
return False
else:
return True
def Rule2(v):
if "variable_name3" and "variable_name4" in v:
return False
elif "variable_name4" and variable_name1 in v:
return False
else:
return True
This is just a pseudo code and therefore not specific but I get the code to compile but I need to know how to call the function Game and whether it's correctly defined since rules will be switched for either Rule1(v) or Rule2(v).
Just pass it in like any other parameter:
def a(x):
return "a(%s)" % (x,)
def b(f,x):
return f(x)
print b(a,10)
Treat function as variable in your program so you can just pass them to other functions easily:
def test ():
print "test was invoked"
def invoker(func):
func()
invoker(test) # prints test was invoked
For passing both a function, and any arguments to the function:
from typing import Callable
def looper(fn: Callable, n:int, *args, **kwargs):
"""
Call a function `n` times
Parameters
----------
fn: Callable
Function to be called.
n: int
Number of times to call `func`.
*args
Positional arguments to be passed to `func`.
**kwargs
Keyword arguments to be passed to `func`.
Example
-------
>>> def foo(a:Union[float, int], b:Union[float, int]):
... '''The function to pass'''
... print(a+b)
>>> looper(foo, 3, 2, b=4)
6
6
6
"""
for i in range(n):
fn(*args, **kwargs)
Depending on what you are doing, it could make sense to define a decorator, or perhaps use functools.partial.
Just pass it in, like this:
Game(list_a, list_b, Rule1)
and then your Game function could look something like this (still pseudocode):
def Game(listA, listB, rules=None):
if rules:
# do something useful
# ...
result = rules(variable) # this is how you can call your rule
else:
# do something useful without rules
A function name can become a variable name (and thus be passed as an argument) by dropping the parentheses. A variable name can become a function name by adding the parentheses.
In your example, equate the variable rules to one of your functions, leaving off the parentheses and the mention of the argument. Then in your game() function, invoke rules( v ) with the parentheses and the v parameter.
if puzzle == type1:
rules = Rule1
else:
rules = Rule2
def Game(listA, listB, rules):
if rules( v ) == True:
do...
else:
do...

What's going on with the lambda expression in this python function? [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
Why does this attempt at creating a list of curried functions not work?
def p(x, num):
print x, num
def test():
a = []
for i in range(10):
a.append(lambda x: p (i, x))
return a
>>> myList = test()
>>> test[0]('test')
9 test
>>> test[5]('test')
9 test
>>> test[9]('test')
9 test
What's going on here?
A function that actually does what I expect the above function to do is:
import functools
def test2():
a = []
for i in range (10):
a.append(functools.partial(p, i))
return a
>>> a[0]('test')
0 test
>>> a[5]('test')
5 test
>>> a[9]('test')
9 test
In Python, variables created in loops and branches aren't scoped. All of the functions you're creating with lambda have a reference to the same i variable, which is set to 9 on the last iteration of the loop.
The solution is to create a function which returns a function, thus scoping the iterator variable. This is why the functools.partial() approach works. For example:
def test():
def makefunc(i):
return lambda x: p(i, x)
a = []
for i in range(10):
a.append(makefunc(i))
return a
Well you can also bind the i to an outer lambda for the lazy.
def p(x, num):
print x, num
def test():
a = []
for i in range(10):
a.append((lambda i :lambda x: p (i, x))(i))
return a
I was always confused as to why this doesn't work. Thanks for the explanation, 'a paid nerd'. I personally prefer this solution:
for i in range(10):
a.append(lambda num, val_i=i: p (val_i, num))
Note the val_i=i default argument of the lambda that enables to capture the instantaneous value of i during the loop whilst still effectively making lambda a function of 1 variable. (BTW: changed your x into num to match p's definition.) I like it better because:
it is very close to the original idea and avoids having to define a new named function, precisely the purpose of a lambda...
avoids importing functools
and avoids imbricating lambdas...
Just did a search and found more detailed explanations for the same problem there: Scope of python lambda functions and their parameters
I asked a similar question, and got two answers. One basically the same as the accepted answer here, and the other which is less clear but slightly more succint.
Dynamically creating a menu in Tkinter. (lambda expressions?)

Categories

Resources