281

Is it possible to pass functions with arguments to another function in Python?

Say for something like:

def perform(function):
    return function()

But the functions to be passed will have arguments like:

action1()
action2(p)
action3(p,r)
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Joan Venge
  • 315,713
  • 212
  • 479
  • 689
  • It's not clear what the original requirements were here. If `perform` is supposed to contain logic that figures out the arguments and uses them to call `function` (including, for example, by receiving those arguments as additional parameters, as in the accepted answer), then [Python function as a function argument?](https://stackoverflow.com/questions/6289646) is a better version of the question. – Karl Knechtel Sep 18 '22 at 14:01
  • On the other hand, if `perform` is expected to be able to call its passed-in `function` without arguments, then arguments for the underlying `action2` and `action3` need to be bound ahead of time - in which case the question is **really** about how to do that binding. [Python Argument Binders](https://stackoverflow.com/questions/277922/python-argument-binders) is the canonical for that. – Karl Knechtel Sep 18 '22 at 14:01

9 Answers9

365

Do you mean this?

def perform(fun, *args):
    fun(*args)

def action1(args):
    # something

def action2(args):
    # something

perform(action1)
perform(action2, p)
perform(action3, p, r)
ncw
  • 1,635
  • 1
  • 18
  • 26
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 14
    What about named parameters? That is, `def action1(arg1, arg2=None, arg3=None)`, how could you pass an argument that you intend to be assigned to arg3, for instance? – ChaimKut Aug 19 '14 at 09:54
  • 7
    perform( fun, **args ), see http://stackoverflow.com/questions/8954746/python-arguments-as-a-dictionary – Mannaggia Jan 23 '15 at 10:45
  • What if `perform` and `action1`, `action2` on different files? @S.Lott – alper Sep 02 '19 at 18:47
  • @alper _import_ them – pfabri Jun 09 '20 at 11:41
  • 3
    Better with both positional and named args: `def f(g, *args, **kwargs): g(*args, **kwargs)` – Trang Oul Aug 30 '21 at 06:13
  • **To do this in C++** using [variadic templates and parameter packs](https://en.cppreference.com/w/cpp/language/parameter_pack), see my code example from my [eRCaGuy_hello_world](https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world) repo here: [variadic_templates_parameter_packs_and_functions.cpp](https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/cpp/variadic_templates_parameter_packs_and_functions.cpp) – Gabriel Staples Sep 30 '22 at 06:11
  • 1
    I don't see a Q&A already covering this for the C++ case, so I've added a Q&A on it here: [How can I pass a function name and variable list of arguments to a function in C++?](https://stackoverflow.com/q/73904873/4561887) – Gabriel Staples Sep 30 '22 at 06:43
182

This is what lambda is for:

def perform(f):
    f()

perform(lambda: action1())
perform(lambda: action2(p))
perform(lambda: action3(p, r))
youri
  • 3,685
  • 5
  • 23
  • 43
Dave
  • 10,369
  • 1
  • 38
  • 35
  • 7
    Also out of curiosity, can you please tell me why lambdas are not good for this case? – Joan Venge Apr 29 '09 at 19:19
  • 12
    lambdas are one of the best features of good programming languages. unfortunately, Python's implementation is severely limited. in this case, however, they fit perfectly – Javier Apr 29 '09 at 19:23
  • 3
    I find that the limited syntax is nearly opaque; they're hard to explain to n00bz. Yes, they do work here, and the confusing features of the syntax are absent. This is -- perhaps -- the only example I've seen of a lambda that's not obscure. – S.Lott Apr 29 '09 at 20:17
  • 12
    So that you could retrieve the passed function's result, wouldn't it be better if Perform() called "return f()" rather than just calling f(). – mhawke Apr 30 '09 at 01:55
  • 1
    I think that the lambda version is quite neat, but oddly in tests I ran it was slower to call functions via the lambda than by the fn(*args) method discussed in another answer. – Richard Shepherd Jan 02 '14 at 16:57
  • wonderful!! exactly what I needed in order to pass parametrized chained functions to other functions. – Reinaldo Gomes Feb 05 '21 at 04:21
  • I see that a defined function can be passed, but is it possible to save the return value in a variable? say something like, `ex = perform(lambda: action1())` I've tried it, but the value is always `None`. – kyrlon Sep 28 '22 at 16:28
54

You can use the partial function from functools like so.

from functools import partial

def perform(f):
    f()

perform(Action1)
perform(partial(Action2, p))
perform(partial(Action3, p, r))

Also works with keywords

perform(partial(Action4, param1=p))
null
  • 878
  • 6
  • 11
  • 2
    `functools.partial` is also more versatile if `perform` needs to hand over further parameters to `f`. E.g., one could call `perform(partial(Action3, p))` and `perform(f)` could do something like `f("this is parameter r")`. – Robert Nov 11 '14 at 20:57
16

Use functools.partial, not lambdas! And ofc Perform is a useless function, you can pass around functions directly.

for func in [Action1, partial(Action2, p), partial(Action3, p, r)]:
  func()
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • 3
    It depends on whether you want the arguments to be evaluated at the call site of Perform or not. – Dave Apr 29 '09 at 18:46
  • @Dave: no it doesn't depends. Provided arguments to partial are always evaluated when calling partial, never at "perform" function. – kriss Oct 06 '22 at 13:52
14

This is called partial functions and there are at least 3 ways to do this. My favorite way is using lambda because it avoids dependency on extra package and is the least verbose. Assume you have a function add(x, y) and you want to pass add(3, y) to some other function as parameter such that the other function decides the value for y.

Use lambda

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = lambda y: add(3, y)
    result = runOp(f, 1) # is 4

Create Your Own Wrapper

Here you need to create a function that returns the partial function. This is obviously lot more verbose.

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# declare partial function
def addPartial(x):
    def _wrapper(y):
        return add(x, y)
    return _wrapper

# run example
def main():
    f = addPartial(3)
    result = runOp(f, 1) # is 4

Use partial from functools

This is almost identical to lambda shown above. Then why do we need this? There are few reasons. In short, partial might be bit faster in some cases (see its implementation) and that you can use it for early binding vs lambda's late binding.

from functools import partial

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = partial(add, 3)
    result = runOp(f, 1) # is 4
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
  • Another (fourth) possible method would be to create a class and make it support `__call__` it can be useful in some bizarre cases like if the final function parameters may be provided asynchronously to the callable context (rather rare), the 3 methods above are much more common. – kriss Oct 06 '22 at 13:58
5

(months later) a tiny real example where lambda is useful, partial not:
say you want various 1-dimensional cross-sections through a 2-dimensional function, like slices through a row of hills.
quadf( x, f ) takes a 1-d f and calls it for various x.
To call it for vertical cuts at y = -1 0 1 and horizontal cuts at x = -1 0 1,

fx1 = quadf( x, lambda x: f( x, 1 ))
fx0 = quadf( x, lambda x: f( x, 0 ))
fx_1 = quadf( x, lambda x: f( x, -1 ))
fxy = parabola( y, fx_1, fx0, fx1 )

f_1y = quadf( y, lambda y: f( -1, y ))
f0y = quadf( y, lambda y: f( 0, y ))
f1y = quadf( y, lambda y: f( 1, y ))
fyx = parabola( x, f_1y, f0y, f1y )

As far as I know, partial can't do this --

quadf( y, partial( f, x=1 ))
TypeError: f() got multiple values for keyword argument 'x'

(How to add tags numpy, partial, lambda to this ?)

denis
  • 21,378
  • 10
  • 65
  • 88
  • If you had this problem, it is something specific to the `f` that you wanted to use, which isn't shown here - so there's no way anyone else could debug the problem. That would be a separate question, anyway. While this is over a decade old, this seems like an appropriate place for a reminder that Stack Overflow is **not a discussion forum**. – Karl Knechtel Sep 18 '22 at 14:04
2

Although all the responses are very accurate and well explained. I want to make a clarification that you also can pass anonymous functions.

def perform(fun, *arg):
    return fun(*arg)

# Pass anonymous function
print(perform(lambda x: x + 1, 3)) # output: 4
print(perform(lambda x, y: x + y + 1, 3, 2)) # output: 6

# Pass defined function
perform(lambda: action1())
perform(lambda: action2(p))
perform(lambda: action3(p, r))

Jorge Tovar
  • 1,374
  • 12
  • 17
  • I see you can pass a defined function, but is it possible to save the return value into a variable? I've attempted this, but the value is always `None` – kyrlon Sep 28 '22 at 13:38
1

Here is a way to do it with a closure:

    def generate_add_mult_func(func):
        def function_generator(x):
            return reduce(func,range(1,x))
        return function_generator

    def add(x,y):
        return x+y

    def mult(x,y):
        return x*y

    adding=generate_add_mult_func(add)
    multiplying=generate_add_mult_func(mult)

    print adding(10)
    print multiplying(10)
Stefan Gruenwald
  • 2,582
  • 24
  • 30
  • In any case one needs to do more than just passing a function to another one closure is the way to go. – jake77 Mar 05 '18 at 12:35
1

I think this is what you're looking for...

def action1(action):
    print(f'doing {action} here!')

def perform(function):
    return function()

perform(lambda : action1('business action'))  

lambda packages up func and args in closure and passes to perform()

Thanks to David Beasley.

Paul Roub
  • 36,322
  • 27
  • 84
  • 93