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 = [] def add_listener(self, callback): ''' 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) notifier.add_listener(Listener(context))
Lambda:
log_listener = lambda event, params: log_event(context, event, params) notifier.add_listener(log_listener)
With partials:
context = get_context() # whatever notifier.add_listener(partial(log_event, context))
Of those three, partial
is the shortest and the fastest. (For a more complex case you might want a custom object though).