Skip to content Skip to sidebar Skip to footer

Closures, Partials And Decorators

I am confused between the three. I understand that closures are functions returned by another functions and have access to the local variables form an enclosing scope example: def

Solution 1:

I think you're confusing purpose with implementation. Creating a closure is a technique for doing things. You can do a bunch of things with closures.

On the other hand, partial and decorator are particular purposes. Maybe they use closures. Maybe they don't. That's an implementation detail, and you don't need to worry about it. What's important is that they achieve the result you want.

Consider a partial. (Ignoring **kwargs.) One way to create it would be to use a closure:

defpartial(f, *args):
    defpf(*rest):
        return f(*args, *rest)
    return pf

But it doesn't have to be that way. For example:

classPartial:def__init__(self, func, args):
        self.args = args
        self.func = func

    def__call__(self, *rest):
        f = self.func
        args = self.args
        return f(*args, *rest)

defpartial(f, *args):
    return Partial(f, args)

There's no closure here, just a variable that holds a reference to the other variables. But I get the partial behavior, so who cares?

The same is true for decorators. A decorator might be a closure, or it might not. For example, one recent question involved running str.format on a function's __doc__ strings. That was just a case of accepting a function object, modifying the __doc__ attribute, and returning the same object. Clearly, there's no closure involved in that.

Solution 2:

Short answer: closure is the mechanism, while functools.partial and decorators are typical uses of that mechanism.

The key difference between a closure and more typical namespaces is that the names and values in the "closed-over" namespace don't vanish when control flow leaves the top-level function. They're preserved in a mini-namespace associated with one instance of the inner function, and which survives as long as that instance does.

functools.partial uses that ability to "remember" some arguments to a pre-existing function. Decorators also typically use that ability for the same reasons.

Note that a decorator is more flexible than that. Any callable that takes one parameter and returns something can serve as a decorator. It doesn't have to make use of Python closures, or even return a function. Many decorators return a callable object instead. (As an aside, a closure and inner function can be simulated using an object for the namespace and a method for the inner function.)

Whatever the decorator returns is assigned to the name the decorated function would have had. (Decorators with parentheses after them, like @decorator('args') ... are slightly more complicated.) The typical decorator syntax:

@decoratordeffunction():
    pass

...is just a shorthand for "define, then decorate and reassign":

def function():
    pass
function = decorator(function)

For an extreme (and mostly useless) example:

defdecorator5(__):
   return5@decorator5defsquare(x):
    return x * x

print(square)  # Prints 5 --- the function is gone.
square(10)     # TypeError: 'int' object is not callable

Solution 3:

The partial version of your decorator would actually be:

defadd_nums(one):
    defadder(one, two):
        return one + two
    return partial(adder, one)

Note that the nested function now only uses explicit parameters, not the closure. You can implement a decorator, which is just a function that takes a function and returns a (usually different) function, using either closures, partial (which is implemented using a closure) or a mix of the two.

Post a Comment for "Closures, Partials And Decorators"