Understanding Python closures and lazy binding

Python closures may be often encountered in interviews or work, and when it comes to Python's delayed binding, it is definitely inseparable from the understanding of closures. Today, I will summarize the concept of closures and a delayed binding interview question .

Python closures

1. What is a closure, a closure must meet the following 3 conditions:

  • Must be a nested function.
  • Closures must return nested functions.
  • Nested functions must reference an outer non-global local free variable.

take a chestnut

# 嵌套函数但不是闭包
def nested():
    def nst():
        print('i am nested func %s' % nested.__name__)
    nst()

# 闭包函数
def closure():
    var = 'hello world' # 非全局局部变量

    def cloe():
        print(var) # 引用var

    return cloe # 返回内部函数


cl = closure()
cl()

2. Closure advantages

  • Avoid using global variables
  • Can provide partial data hiding
  • Can provide a more elegant object-oriented implementation

The advantages 1 and 2 will not be mentioned, it is easy to understand. Regarding the third, for example, when there are few methods implemented in a class, or when there is only one method, you can choose to use a closure.

take a chestnut

# 用类实现一个加法的类是这样
class _Add(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a + self.b

# 用闭包实现
def _Add(a):
    def add(b):
        return a + b

    return add  

ad = _Add(1) # 是不是很像类的实例化
print(ad(1)) # out:2
print(ad(2)) # out:3
print(ad(3)) # out:4

That's pretty much the concept of closures.


Python lazy binding

To illustrate with a topic:

def multipliers():
    return [lambda x : i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [6, 6, 6, 6]

In fact, the purpose of this question may be to output: [0, 2, 4, 6], how to improve it to output this result?

def multipliers():
    # 添加了一个默认参数i=i
    return [lambda x, i=i: i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [0, 2, 4, 6]

multipliers is a closure function

1.def multipliers():
2.    return [lambda x : i*x for i in range(4)]
3.  # multipliers内嵌套一个匿名函数
4.  # 该匿名函数引用外部非全局变量 i
5.  # 返回该嵌套函数
6.print [m(2) for m in multipliers()]

Let's explain why the output is [6,6,6,6].

Run the code, the code runs from line 6, the interpreter encounters a list comprehension, loops to take the value in the multipliers() function, and the multipliers() function returns a list object, there are 4 elements in this list, each Each element is an anonymous function (in fact, it is not completely accurate to say that it is 4 anonymous functions, but it is actually the value calculated by the 4 anonymous functions, because the following for i loop not only loops 4 times, but also provides i The variable reference of , waits for the end of the 4 loops, i points to a value i=3, at this time, the anonymous function starts to refer to i=3 and calculates the result. So [6,6,6,6] will appear, because the anonymous function The i in the function does not immediately refer to the value of i in the following loop, but when the nested function is run, the value of i will be looked up. This feature is also called delayed binding)

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码,并不是准确的):

def multipliers():
    return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]

Because the Python interpreter encounters lambda (similar to def), it just defines an anonymous function object and saves it in memory. Only when the anonymous function is called, the internal expression will be run, and for i in range (4) is another expression, and the lambda function will start to run after the expression has finished running. At this time, i points to 3, and x points to 2.


Then let's take a look, adding an i=i, what happened?

def multipliers():
    # 添加了一个默认参数i=i
    return [lambda x, i=i: i*x for i in range(4)]

After adding an i=i, a default parameter is added to the anonymous function, and the default parameter in the python function must be initialized when the python interpreter encounters the def(i=i) or lambda keyword. Parameters, at this time for i in range(4), every time the loop is performed, the default parameter i of the anonymous function needs to find a reference to i. When i=0, the default parameter value of the first anonymous function is 0, i= 1, the default parameter value of the second anonymous function is 1, and so on.

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码只是为了理解):

def multipliers():
    return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
# x的引用是2 所以output的结果就是:[0,2,4,6]

Of course, your i=i can also be changed to a=i.

def multipliers():
    return [lambda x,a=i: a * x for i in range(4)]

Python's delayed binding actually refers to the external variable i only when the nested function is running. When it is not running, it does not look for the value of i. This is the first function. Why is the output result [ 6,6,6,6] reasons.


The above is my understanding of Python closures and delayed binding.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325384801&siteId=291194637