Closure summary in Python

  • 2020-04-02 14:09:36
  • OfStack

Someone left a comment on this article of mine the other day (link: #), and it was not clear about one of the closures and the use of re. Sub. I did a search on my blog and found that I hadn't written anything about closures, so I decided to summarize and improve on Python on my blog.

1. The concept of closures

Let's start with the basic concept. What is a closure? Here's the wiki explanation:


In computer science, closures ( Closure ) is a lexical closure ( Lexical Closure ) is a function that refers to a free variable. The referenced free variable will exist with the function, even if it has left the context in which it was created. So, there is another way of saying that a closure is an entity composed of a function and its associated reference environment. Closures can have multiple instances at run time, and different reference environments and the same combination of functions can produce different instances.
....

There are two key points mentioned above: free variables and functions, which I'll come back to later. Again, the meaning of "closure" has to be repeated. It can be understood figuratively as a closed package, which is a function, and of course the logic corresponding to the function. The contents of the package are free variables, which can wander around with the package. Of course, the premise is that the package is created.

In Python, A closure is when you call A function A that returns A function B to you. This function B is called a closure. The arguments you pass when you call function A are free variables.

For example:


def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func bb = func('the5fire')
bb(26)  # >>> name: the5fire age: 26

This produces a closure called inner_func when func is called, and the closure holds a free variable called name, so this also means that when the func life cycle is over, the name variable will still exist because it is referenced by the closure and will not be recycled.

Also, closures are not a unique concept in Python; all languages that make functions first-class citizens have closures. Closures can also be used in a first-class citizen language like Java, but they have to be implemented in a class or interface.

See the last link for more conceptual information.

2. Why use closures

Based on the above, I don't know if the reader feels that this thing is similar to the class, the similarity is that they both provide the encapsulation of the data. The difference is that the closure itself is a method. As with classes, we often abstract generic things into classes (and, of course, real-world business modeling) to reuse generic functionality. The same goes for closures, which are a good choice when abstractions of function granularity are needed.

At this point a closure can be understood as a read-only object, which you can pass a property to, but which only gives you an interface to execute. So in our programs, we often need a function object -- a closure -- to help us accomplish a common function, such as a decorator, which we'll talk about later.

Use closures

The first scenario, which is very important and very common in python, is decorator. Python provides decorator with a very friendly "syntax sugar" -- @, which makes it very convenient to use decorator. Without much explanation of the principle of decoration, you add @decorator_func to a function func, which is equivalent to decorator_func(func):


def decorator_func(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper @decorator_func
def func(name):
    print 'my name is', name # Is equivalent to
decorator_func(func)

In the decorator example, the wrapper holds the external func argument and is able to accept the argument passed from the outside, which is passed to func intact and returns the execution result.

This is a simple example, but a little more complicated can have multiple closures, such as the commonly used decorator for LRUCache, which accepts the @lru_cache parameter (expire=500). The implementation is just a nest of two closures:


def lru_cache(expire=5):
    # The default 5s timeout
    def func_wrapper(func):
        def inner(*args, **kwargs):
            # cache To deal with bala bala bala
            return func(*args, **kwargs)
        return inner
    return func_wrapper @lru_cache(expire=10*60)
def get(request, pk)
    # Omit specific code
    return response()

Those of you who don't know much about closures should be able to understand this code, which is one of the interview questions we often ask in previous interviews.
The second scenario is based on a feature of closures called lazy evaluation. This application is more common when database access, such as:


# Pseudocode diagram class QuerySet(object):
    def __init__(self, sql):
        self.sql = sql
        self.db = Mysql.connect().corsor()  # Pseudo code     def __call__(self):
        return db.execute(self.sql) def query(sql):
    return QuerySet(sql) result = query("select name from user_app")
if time > now:
    print result  # This is when the database access is performed

The above example, which is not a good example, shows the ability to perform lazy evaluation with closures, but the result returned by query above is not a function, but a class with function function. If you're interested, take a look at the implementation of Django's queryset, which works in a similar way.

The third scenario, where you need to assign a parameter to a function in advance, is of course a good solution for accessing functools.parial in Python, but you can do it with closures.


def partial(**outer_kwargs):
    def wrapper(func):
        def inner(*args, **kwargs):
            for k, v in outer_kwargs.items():
                kwargs[k] = v
            return func(*args, **kwargs)
        return inner
    return wrapper @partial(age=15)
def say(name=None, age=None):
    print name, age say(name="the5fire")
# Of course with functools It's a lot easier than that
# Only need to: functools.partial(say, age=15)(name='the5fire')

This seems like another far-fetched example, but it's still a practice for closures.

In conclusion, closures are easy to understand and are widely used in Python. This article is a summary of closures. If you have any questions, please feel free to leave a comment.


Related articles: