Python way to parameterize a function (without lambda)

This is a question about programming style: what is the most "pythonic" way to parameterize a function (is this even the right word for it?)

Let's say that I have a function (like the ODE solver) that takes as an argument another function of two arguments (like the ODE itself).

def solver(fun):
   # Implementation goes here
   # fun is a function handle that accepts two args e.g. fun(t,y)

      

However, the function I would like to pass in solver

is parameterized with the third value

def myFun(t,y,A):
   # Implementation goes here

      

I ran into this situation using functions lambda

like this:

A = 4
solution = solver(lambda t,y:myFun(t,y,A))

      

I recently saw several posts on the Internet saying that I avoid lambda

like the plague and that Guido himself regrets allowing this feature. If lambda

really awful, what's the best "pythonic" way to implement the above? Without lambda

I am facing the problem of not being able to access the global namespace, that is, I want to do this:

A = 4
def myFunNotLambda(t,y):
    return myFun(t,y,A)
solution = solver(myFunNotLambda)

      

but the only way to do it seems to be doing global A

, which is definitely much worse than usinglambda

+3


source to share


3 answers


The "avoid lambda

like the plague" advice is a gross exaggeration at best and pure FUD at worst β€” grepping lambda

in the Python standard library shows literally thousands of matches.

While it is true that Guido expressed later concerns about the lambda-dominated coding style, it was in the context of (more) using functional constructs such as map

and reduce

, which are better expressed in Python using lists and generator expressions.

When it comes to making temporary functions like the ones you want, lambda

there is nothing wrong with using them ; on the contrary, it is the best tool available for the job. If you still want to avoid the keyword lambda

, you can use nested def

:



def my_particular_fun(t, y):
    return my_fun(t, y, 4)

solution = solver(my_particular_fun)

      

As presented in other answers can also be used functools.partial

to emulate a lambda, although this comes at the cost of fixing the name of the third parameter to my_fun

.

+3


source


You can use for example: functools.partial

from functools import partial

A = 4
solution = solver(partial(myFun,A=A))
      



partial(..)

builds a given function (here myFunc

) another function where the parameter A

has a default c A

.

+5


source


An efficient way to do this is to use functools.partial

, but, as pointed out, partial

allows you to "freeze" the final arguments. If you need something more, you can easily implement your version with a closure .

This approach is actually slightly more efficient than using an object partial

, because when you call partial

it still has to call the original function you are passing in, so each call partial

results in two calls, and Python function / method calls are relatively slow. If you're interested, see the Python source code forfunctools.partial

.

In this example, I made the A

second (pseudo) parameter of the function, as partial

it handles the case when it's the last argument just fine.

def my_partial(param):
    A = param
    print('Creating two_arg_func with A ==', A)
    def two_arg_func(t, y):
        # Do something with all the args
        return 'args', t, A, y
    return two_arg_func

def test(f):
    for u in range(10, 50, 10):
        print(f(u, u + 5))

test(my_partial(7))

      

Output

Creating two_arg_func with A == 7
('args', 10, 7, 15)
('args', 20, 7, 25)
('args', 30, 7, 35)
('args', 40, 7, 45)

      


We don't really need param

to my_partial

, we can just use the passed in arg since it's local to my_partial

:

def my_partial(A):
    print('Creating two_arg_func with A ==', A)
    def two_arg_func(t, y):
        return 'args', t, A, y
    return two_arg_func

      


From your comments, I now understand what you want to change A

. Of course you can do this by calling again partial

or my_partial

, but if you want to change a A

lot, that is not as effective.

Your comments indicate what you want to change A

in the global context so you can just use the global one. You don't need to insert the modifying code A

into the global context, you can wrap it in a function, but of course you will need to use the directive global

in the function that modifies A

. You don't need a directive global

in any function that just reads a value A

.

Here's a short demo.

def two_arg_func(t, y):
    # Do something with the args and with A
    return 'args', t, A, y

def solve(f):
    for u in range(10, 40, 10):
        print('SOLVER', f(u, u + 5))

def test(f):
    global A
    for A in range(7, 10):
        print(A)
        solve(f)

test(two_arg_func)

      

Output

7
SOLVER ('args', 10, 7, 15)
SOLVER ('args', 20, 7, 25)
SOLVER ('args', 30, 7, 35)
8
SOLVER ('args', 10, 8, 15)
SOLVER ('args', 20, 8, 25)
SOLVER ('args', 30, 8, 35)
9
SOLVER ('args', 10, 9, 15)
SOLVER ('args', 20, 9, 25)
SOLVER ('args', 30, 9, 35)

      


However , this previous solution is somewhat unsatisfactory since the main question of your question was how to do it without using the global. So, here's a small variation on this code. Instead of putting it A

in the global namespace, we attach it to two_arg_func

as a function attribute. We can do this because a Python function is an object and already has a whole bunch of attributes; two of which are familiar to you: __name__

and __doc__

. Anyway, here's the new code, it prints the same output as the previous version.

def two_arg_func(t, y):
    A = two_arg_func.A
    # Do something with the args and with A
    return 'args', t, A, y

def solve(f):
    for u in range(10, 40, 10):
        print('SOLVER', f(u, u + 5))

def test(f):
    for A in range(7, 10):
        print(A)
        f.A = A
        solve(f)

test(two_arg_func)

      

+1


source







All Articles