81

How can I bind arguments to a Python function so that I can call it later without arguments (or with fewer additional arguments)?

For example:

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

add_5 = magic_function(add, 5)
assert add_5(3) == 8

What is the magic_function I need here?


It often happens with frameworks and libraries that people accidentally call a function immediately when trying to give arguments to a callback: for example on_event(action(foo)). The solution is to bind foo as an argument to action, using one of the techniques described here. See for example How to pass arguments to a Button command in Tkinter? and Using a dictionary as a switch statement in Python.

Some APIs, however, allow you to pass the to-be-bound arguments separately, and will do the binding for you. Notably, the threading API in the standard library works this way. See thread starts running before calling Thread.start. If you are trying to set up your own API like this, see How can I write a simple callback function?.

Explicitly binding arguments is also a way to avoid problems caused by late binding when using closures. This is the problem where, for example, a lambda inside a for loop or list comprehension produces separate functions that compute the same result. See What do lambda function closures capture? and Creating functions (or lambdas) in a loop (or comprehension).

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Dustin Getz
  • 21,282
  • 15
  • 82
  • 131

7 Answers7

92

functools.partial returns a callable wrapping a function with some or all of the arguments frozen.

import sys
import functools

print_hello = functools.partial(sys.stdout.write, "Hello world\n")

print_hello()
Hello world

The above usage is equivalent to the following lambda.

print_hello = lambda *a, **kw: sys.stdout.write("Hello world\n", *a, **kw)
jpmc26
  • 28,463
  • 14
  • 94
  • 146
Jeremy
  • 1
  • 85
  • 340
  • 366
75

Using functools.partial:

>>> from functools import partial
>>> def f(a, b):
...     return a+b
... 
>>> p = partial(f, 1, 2)
>>> p()
3
>>> p2 = partial(f, 1)
>>> p2(7)
8
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Matthew Trevor
  • 14,354
  • 6
  • 37
  • 50
  • 14
    this is a better example than the accepted answer, because it shows partial binding. – AShelly Apr 25 '14 at 14:24
  • ...though it would be better if the function `f` itself did not process its two arguments symmetrically so that it was obvious which of the two arguments is bound in the partial binding (which I assume is the first) - as in `def f(a, b): return a + 2*b` – Dan Nissenbaum Jun 09 '21 at 20:28
14

If functools.partial is not available then it can be easily emulated:

>>> make_printer = lambda s: lambda: sys.stdout.write("%s\n" % s)
>>> import sys
>>> print_hello = make_printer("hello")
>>> print_hello()
hello

Or

def partial(func, *args, **kwargs):
    def f(*args_rest, **kwargs_rest):
        kw = kwargs.copy()
        kw.update(kwargs_rest)
        return func(*(args + args_rest), **kw) 
    return f

def f(a, b):
    return a + b

p = partial(f, 1, 2)
print p() # -> 3

p2 = partial(f, 1)
print p2(7) # -> 8

d = dict(a=2, b=3)
p3 = partial(f, **d)
print p3(), p3(a=3), p3() # -> 5 6 5
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 1
    As a historical note: the `functools` standard library was introduced in 2.5, which first came out in 2006. This answer was written shortly before the 2.5.3 and 2.5.4 updates. A fair number of users would have still been on 2.4, possibly even 2.3. – Karl Knechtel Aug 17 '22 at 22:08
13

lambdas allow you to create a new unnamed function with fewer arguments and call the function:

>>> def foobar(x, y, z):
...     print(f'{x}, {y}, {z}')
... 
>>> foobar(1, 2, 3)  # call normal function
1, 2, 3
>>> bind = lambda x: foobar(x, 10, 20)  # bind 10 and 20 to foobar
>>> bind(1)
1, 10, 20
>>> bind = lambda: foobar(1, 2, 3)  # bind all elements
>>> bind()
1, 2, 3

You can also use functools.partial. If you are planning to use named argument binding in the function call this is also applicable:

>>> from functools import partial
>>> barfoo = partial(foobar, x=10)
>>> barfoo(y=5, z=6)
10, 5, 6

Note that if you bind arguments from the left you need to call the arguments by name. If you bind from the right it works as expected.

>>> barfoo(5, 6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foobar() got multiple values for argument 'x'
>>> f = partial(foobar, z=20)
>>> f(1, 1)
1, 1, 20
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
7

This would work, too:

def curry(func, *args):
    def curried(*innerargs):
       return func(*(args+innerargs))
    curried.__name__ = "%s(%s, ...)" % (func.__name__, ", ".join(map(str, args)))
    return curried

>>> w=curry(sys.stdout.write, "Hey there")
>>> w()
Hey there
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 2
    This is not a curry. Currying is for functions with multiple arguments. Curry works like this: Given `g = curry(f)`, we have `f(x1,x2,...) = g(x1)(x2)...` – Gavin Haynes May 21 '19 at 23:43
  • @GavinHaynes You are correct - "currying" is supposed to mean the transformation of `f` into `g`, but it's commonly used to mean *partial application* (i.e. "binding"). For actual currying, see [Currying decorator in Python](https://stackoverflow.com/questions/9458271). For the related pedantry, see [What is the difference between currying and partial application?](https://stackoverflow.com/questions/218025). – Karl Knechtel Aug 17 '22 at 22:20
1

Functors can be defined this way in Python. They're callable objects. The "binding" merely sets argument values.

class SomeFunctor( object ):
    def __init__( self, arg1, arg2=None ):
        self.arg1= arg1
        self.arg2= arg2
    def __call___( self, arg1=None, arg2=None ):
        a1= arg1 or self.arg1
        a2= arg2 or self.arg2
        # do something
        return

You can do things like

x= SomeFunctor( 3.456 )
x( arg2=123 )

y= SomeFunctor( 3.456, 123 )
y()
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 1
    interesting pattern, lots of boilerplate for trivial functions though. – Dustin Getz Nov 10 '08 at 15:02
  • 1
    You can cut it down to something considerably simpler, depending on your actual use cases. The original question provides no guidance on how the binding occurs. Without specifics, there are too many bases to cover. – S.Lott Nov 10 '08 at 15:11
  • Using classes like this [is frowned upon by some, with clear reasoning](https://www.youtube.com/watch?v=o9pEzgHorH0). – Karl Knechtel Aug 19 '22 at 11:58
1

The question asks generally about binding arguments, but all answers are about functions. In case you are wondering, partial also works with class constructors (i.e. using a class instead of a function as a first argument), which can be useful for factory classes. You can do it as follows:

from functools import partial

class Animal(object):
    def __init__(self, weight, num_legs):
        self.weight = weight
        self.num_legs = num_legs
        
animal_class = partial(Animal, weight=12)
snake = animal_class(num_legs = 0)
print(snake.weight) # prints 12
Ataxias
  • 1,085
  • 13
  • 23
  • This seems more like a footnote to an existing answer. Classes are *callable*, so it's not really surprising - under Python's duck-typing philosophy - that tools that work with a function would also work with class instantiation. – Karl Knechtel Aug 17 '22 at 22:04