Understanding Lambda/Closure, Part 3 - Python Supports for Lambda/Closure



You can use def to define a function in Python. Every function is an instance of function, so it can be assigned to other variables. For example:

def max(m, n):
    return m if m > n else n

print(max(10, 3)) # print 10

maximum = max
print(maximum(10, 3)) # print 10
   
If you want to create an anonymous function in Python, you can use a lambda expression. For example:

max = lambda m, n: m if m > n else n
print(max(10, 3)) # print 10

Different languages provide function/lambda supports with different syntax. Python's clean syntax looks better to express a function than JavaScript. You can see the cleanness difference in the following examples:

// Define a function: JavaScript
function max(n, n) {
    return m > n ? m : n;
}

# Define a function: Python
def max(m, n):
    return m if m > n else n

// Create an anonymous function: JavaScript
function(n, n) {
    return m > n ? m : n;
};

# Create an anonymous function: Python
lambda m, n: m if m > n else n

Let's see another use of lambda/closure. If your function needs a time-consuming resource, reusing it to avoid performance issues is often a consideration. One of the ways is to create the resource globally and reuse it in the function. A global resource, however, is often a bad practice. We can prepare the resource in a function, create a closure to catch it, and return the closure from that function. For example:

import math
def prepare_factor(max):
    # Creating a prime table is time-consuming.
    primes = [i for i in range(2, max) if prime[i] == 1]

    def factor(num):
        while primes[i] ** 2 <= num:
            if num % primes[i] == 0:
                list.append(primes[i])
                num //= primes[i]
            else:
                i += 1

    return factor

factor = prepare_factor(1000)
print(factor(100)) # print [2, 2, 5, 5]

In the above example, the inner function factor creates a closure to catch the primes variable of the enclosing function. Because a function is an object, you can return it from a function. The lifecycle of the primes variable is now associated with the returned function. We don't expose the primes variable globally, yet still can reuse the resource.

Until now, we've seen that, when a function is an object, it can be...
  • Referred by other variables.
  • Not only called, but also passed into a function to replace the algorithm in a reusable template.
  • A closure to catch free variables (resource) and returned from a function.
But, Python's closures have an important limitation. You cannot assign to a free variable. In other words, closures in Python are read-only. For example:

def func():
    x = 10
    def getX():
        return x
    def setX(n):
        x = n   # Create a local variable x
    return (gegX, setX)

getX, setX = func()
getX() # 10

setX(20)
getX(10) # still 10

In Python, the first time when you assign to a variable, you create a new local variable. In the above example, when you call setX, you actually create a local variable x in setX, not assign the parameter n to the local variable of func. That's why you get 10 in the last statement.

Fortunately, after Python 3, you can use the global or nonlocal keywords to avoid this condition. For example:

def func():
    x = 10
    def getX():
        return x
    def setX(n):
        nonlocal x = n
    return (gegX, setX)

getX, setX = func()
getX() # 10

setX(20)
getX(10) # 20

In the above example, the nonlocal keyword says x is not a local variable. The Python interpreter looks the enclosing function and understands x is caught from the local variable x of func. This time when you call setX, you definitely change the value of the local variable x in func.

We've talked about lambda/closure supports in JavaScript and Python. They are both dynamically-typed languages. If we move into statically-typed languages, however, what issues should we concern? Looking at a statically-typed language with lambda/closure supports to learn some experience seems a good idea. That's we'll talk about Scala in the next article.