user1865341 - 1 year ago 120
Python Question

# How does the functools partial work in Python?

I am not able to get my head on how the partial works in functools.
I have the following code from here:

``````>>> sum = lambda x, y : x + y
>>> sum(1, 2)
3
>>> incr = lambda y : sum(1, y)
>>> incr(2)
3
>>> def sum2(x, y):
return x + y

>>> incr2 = functools.partial(sum2, 1)
>>> incr2(4)
5
``````

Now in the line

``````incr = lambda y : sum(1, y)
``````

I get that whatever argument I pass to
`incr`
it will be passed as
`y`
to
`lambda`
which will return
`sum(1, y)`
i.e
`1 + y`
.

I understand that. But I didn't understand this
`incr2(4)`
.

How does the
`4`
gets passed as
`x`
in partial function? To me,
`4`
should replace the
`sum2`
. What is the relation between
`x`
and
`4`
?

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
``````

So, by calling `partial(sum2, 4)` you create a new function (a callable, to be precise) that behaves like `sum2`, but has one positional argument less. That missing argument is always substituted by `4`, so that `partial(sum2, 4)(2) == sum2(4, 2)`

As for why it's needed, there's a variety of cases. Just for one, suppose you have to pass a function somewhere where it's expected to have 2 arguments:

``````class EventNotifier(object):
def __init__(self):
self._listeners = []

''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...

def notify(self, event, *params):
for f in self._listeners:
f(event, params)
``````

But a function you already have needs access to some third `context` object to do its job:

``````def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
``````

So, there are several solutions:

A custom object:

``````class Listener(object):
def __init__(self, context):
self._context = context

def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)

``````

Lambda:

``````log_listener = lambda event, params: log_event(context, event, params)
``````

With partials:

``````context = get_context()  # whatever
``````

Of those three, `partial` is the shortest and the fastest. (For a more complex case you might want a custom object though).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download