December 30, 2015

Closures and Decorators in Python

Closures basically mean that an inner function defined in an outer function remembers what its enclosing name space (or name spaces) looked like at definition time. The variables defined in this name space can later be accessed by the inner function:

def closure():
    x = 1
    def inner(y):
        return y - x
    return inner

print(closure()(5))

4

Closures also work, when an variable is passed to the outer function:

def otherClosure(x):
    def inner(y):
        return y - x
    return inner

print(otherClosure(1)(5))

4

Before we define the decorator, let us define a simple function:

def sub(x, y):
    return x - y

print(sub(5, 4))

2

print(sub(5, 4))

-1

With the closures concept in mind, we can define a decorator, which is nothing else than a function that takes another function and extends this function with some new functionality. The function to be decorated is passed to the extending inner function via the name space of the outer function. In our example, the sub function shall be modified so that it either returns x - y, or 0, if x - y would be negative.

def wrapper(func): # outer function
    def zeroIfNegative(x, y): # inner function
        if x - y > 0:
            return func(x, y)
        else:
            return 0
    return zeroIfNegative

The decorator is applied to the function by calling the decorator with the function as an argument:

sub = wrapper(sub)
print(sub(5, 6))

0

print(sub(5, 4))

1

As an alternative to the previously described way of applying the decorator, the @ symbol can be used:

@wrapper
def sub2(x, y):
    return x - y

print(sub2(5, 6))

0

print(sub2(5, 4))

1

Decorators can have arguments. These need to be passed to the decorator via another(!!) outer function:

def wrappedWrapper(okText, nioText):
    def wrapper(func):
        def zeroIfNegative(x, y):
            if x - y > 0:
                print(okText)
                return func(x, y)
            else:
                print(nioText)
                return 0
        return zeroIfNegative
    return wrapper

@wrappedWrapper("Difference > 0.", "Difference < 0")
def sub3(x, y):
    return x - y

# OR: sub3 = wrappedWrapper("Difference > 0.", "Difference < 0")(sub3)

print(sub3(5, 6))

Difference < 0 0

print(sub3(5, 4))

Difference > 0. 1

© holger 2015 - 2020 |