Search code examples
pythonlambdafunctools

Unexpected result when changing list of functions (lambda)


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?


Solution

  • 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