Home > Article > Backend Development > Talk about closures in Python - Closure
Closure in Python is not a concept that can be understood immediately, but as you learn more, you need to understand such a thing no matter what.
The concept of closure
Let’s try to understand closure conceptually.
In some languages, when another function can be (nested) defined in a function, if the inner function refers to the variables of the outer function, a closure may occur. Closures can be used to create an association between a function and a set of "private" variables. These private variables maintain their persistence across multiple calls to a given function.
——Wikipedia)
In easier-to-understand terms, when a function is returned as an object, external variables are entrained, forming a closure. See examples.
def make_printer(msg): def printer(): print msg # 夹带私货(外部变量) return printer # 返回的是函数,带私货的函数 printer = make_printer('Foo!') printer()
Programming languages that support using functions as objects generally support closures. Such as Python, JavaScript.
How to understand closure
What is the significance of closure? Why is closure needed?
I personally think that the meaning of closure is that it carries external variables (private goods). If it does not carry private goods, It is no different from an ordinary function. The same function carries different private goods to achieve different functions. In fact, you can also understand it this way. The concept of closure is very similar to that of interface-oriented programming. You can understand closure as a lightweight interface encapsulation.
The interface defines a set of constraint rules for method signatures.
def tag(tag_name): def add_tag(content): return "<{0}>{1}</{0}>".format(tag_name, content) return add_tag content = 'Hello' add_tag = tag('a') print add_tag(content) # <a>Hello</a> add_tag = tag('b') print add_tag(content) # <b>Hello</b>
In this example, we want a function to add tags to content, but the specific tag_name will be determined based on actual needs. The interface for external calls has been determined, which is add_tag(content). If implemented in an interface-oriented manner, we will first write add_tag as an interface, specify its parameters and return type, and then implement add_tag of a and b respectively.
But in the concept of closure, add_tag is a function, which requires two parameters: tag_name and content, but the parameter tag_name is packed away. So you can tell me how to pack it at the beginning and take it away.
The above example is not too vivid. In fact, the concept of closure is also very common in our life and work. For example, when dialing on a mobile phone, you only care about who the call is to, and you don't worry about how each brand of mobile phone implements it and what modules are used. Another example is when you go to a restaurant to eat, you only need to pay to enjoy the service. You don't know how much gutter oil was used in the meal. These can be regarded as closures, which return some functions or services (making phone calls, dining), but these functions use external variables (antennas, drain oil, etc.).
You can also think of a class instance as a closure. When you construct this class, you use different parameters. These parameters are the packages in the closure. The external methods provided by this class are the functions of the closure. But a class is much larger than a closure, because a closure is just a function that can be executed, but a class instance may provide many methods.
When to use closures
In fact, closures are very common in Python, but you just didn’t pay special attention to the fact that this is a closure. For example, the decorator in Python, if you need to write a decorator with parameters, a closure will generally be generated.
Why? Because Python’s decorator is a fixed function interface form. It requires that your decorator function (or decorator class) must accept a function and return a function:
# how to define def wrapper(func1): # 接受一个callable对象 return func2 # 返回一个对象,一般为函数 # how to use def target_func(args): # 目标函数 pass # 调用方式一,直接包裹 result = wrapper(target_func)(args) # 调用方式二,使用@语法,等同于方式一 @wrapper def target_func(args): pass result = target_func()
So what if your decorator takes parameters? Then you need to wrap another layer on the original decorator , used to receive these parameters. After these parameters (private goods) are passed to the inner decorator, the closure is formed. So when your decorator requires custom parameters, a closure will generally be formed. (Exception for class decorators)
def html_tags(tag_name): def wrapper_(func): def wrapper(*args, **kwargs): content = func(*args, **kwargs) return "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content) return wrapper return wrapper_ @html_tags('b') def hello(name='Toby'): return 'Hello {}!'.format(name) # 不用@的写法如下 # hello = html_tag('b')(hello) # html_tag('b') 是一个闭包,它接受一个函数,并返回一个函数 print hello() # <b>Hello Toby!</b> print hello('world') # <b>Hello world!</b>
For a more in-depth analysis of decorators, you can read another blog I wrote.
Go a little deeper
In fact, you don’t have to go too deep. If you understand the concepts above, many codes that seem to be a headache are just that.
Let’s take a look at what the closure package looks like. In fact, the closure function has an additional __closure__ attribute compared to the ordinary function, which defines a tuple to store all cell objects. Each cell object stores all the external variables in the closure one by one.
>>> def make_printer(msg1, msg2): def printer(): print msg1, msg2 return printer >>> printer = make_printer('Foo', 'Bar') # 形成闭包 >>> printer.__closure__ # 返回cell元组 (<cell at 0x03A10930: str object at 0x039DA218>, <cell at 0x03A10910: str object at 0x039DA488>) >>> printer.__closure__[0].cell_contents # 第一个外部变量 'Foo' >>> printer.__closure__[1].cell_contents # 第二个外部变量 'Bar'
The principle is that simple.