Home  >  Article  >  Backend Development  >  A brief analysis of several advanced syntax concepts of Python (lambda expression closure decorator)

A brief analysis of several advanced syntax concepts of Python (lambda expression closure decorator)

高洛峰
高洛峰Original
2017-01-23 14:38:461430browse

1. Anonymous function
Anonymous function refers to a function that is not bound to any identifier. It is mostly used in the field of functional programming languages. Typical application scenarios:
1) Passed to higher-level functions as parameters Functions (higher-order function), such as the built-in functions filter/map/reduce in python are all typical higher-order functions
2) As the return value of the higher-order function (although the "value" here is actually It is a function object)
Compared with named functions (named functions), if the function is called only once or a limited number of times, the anonymous function is syntactically more lightweight.
In terms of specific syntax, python supports anonymous functions whose function body is an expression through lambda syntax, that is: python's lambda expression is essentially an anonymous function, but its function body can only be an expression and cannot contain other statements.
In addition, high-level dynamic languages ​​often use anonymous functions to implement advanced syntax such as closures or decorators.
In some cases, the use of lambda expressions makes python programs look very concise. For example, the following is a code example for sorting dict elements according to value:

>>> foo = {'father' : 65, 'mother' : 62, 'sister' : 38, 'brother' : 29, 'me' : 28}
>>> sorted(foo.iteritems(), key=lambda x: x[1])
[('me', 28), ('brother', 29), ('sister', 38), ('mother', 62), ('father', 65)]

2. Closure
A closure is essentially a function or function that contains its referring environment (referencing environment) Function references, the "reference environment" here is usually maintained by a table that stores references to non-local variables that the function body accesses.
Compared with function pointers in C language, closures allow nested functions to access non-local variables outside their scope, which is related to the Python interpreter's scope search rules for variables (Python supports LEGB's search rules, If you want to learn more, you can refer to the detailed explanation of scope and search rules in Chapter 17 of "Learning Python" 4th edition, or check this article for a quick understanding).
For languages ​​whose runtime memory allocation model creates local variables on a linear stack (typically such as C language), it is usually difficult to support closures. Because in the underlying implementation of these languages, if the function returns, the local variables defined in the function will be destroyed as the function stack is recycled. However, the underlying implementation of closures requires that the non-local variables they want to access remain valid when the closure is executed until the life cycle of the closure ends. This means that these non-local variables can only be accessed when they are determined to no longer be accessed. They can only be destroyed when they are used, not when the function that defines these variables returns. Therefore, languages ​​that naturally support closures usually use garbage collection to manage memory, because the gc mechanism ensures that variables will be destroyed by the system and their memory space will be reclaimed only when they are no longer referenced.
In terms of specific syntax, closures are usually accompanied by nested function definitions. Taking Python as an example, a simple closure example is as follows:

#!/bin/env python
#-*- encoding: utf-8 -*-
 
def startAt_v1(x):
 def incrementBy(y):
  return x + y 
 print 'id(incrementBy)=%s' % (id(incrementBy))
 return incrementBy
 
def startAt_v2(x):
 return lambda y: x + y 
 
if '__main__' == __name__:
 c1 = startAt_v1(2)
 print 'type(c1)=%s, c1(3)=%s' % (type(c1), c1(3))
 print 'id(c1)=%s' % (id(c1))
  
 c2 = startAt_v2(2)
 print 'type(c2)=%s, c2(3)=%s' % (type(c2), c2(3))

The execution result is as follows:

id(incrementBy)=139730510519782
type(c1)=<type &#39;function&#39;>, c1(3)=5
id(c1)=139730510519782
type(c2)=<type &#39;function&#39;>, c2(3)=5

In the above example, startAt_v1 and startAt_v2 both implement closures, among which: v1 uses nesting Define function implementation; v2 is implemented with lambda expression/anonymous function.
We take v1 as an example to illustrate the closure:
1) The function startAt_v1 accepts 1 parameter and returns a function object, and the behavior of this function object is implemented by the nested defined function incrementBy.
2) For the function incrementBy, the variable x is a so-called non-local variable (because x is neither a local variable defined by the function nor a global variable in the ordinary sense). incrementBy implements specific function behavior and returns .
3) The return value received by c1 at the main entrance is a function object. From id(incrementBy) == id(c1), it can be concluded that the object "pointed to" by c1 and the object "pointed to" by the function name incrementBy are actually the same one. function object.
4) Benefiting from Python’s support for closures, compared with objects of ordinary functions, the object pointed to by c1 can access non-local variables that are not within the scope of its function, and this variable is the outer wrapper function of incrementBy. The input parameters of startAt_v1 are provided. Therefore, the function object pointed to by c1 has a "memory" function for the input parameters of its outer wrapping function. When creating a closure by calling the outer wrapping function, different input parameters are used by the inner function. Maintained as a reference environment.
5) When calling c1(3), the incoming parameters are calculated together with the parameters of the outer packaging function that reference the environment maintenance to obtain the final result.
The above step analysis illustrates the basic principle of a closure from creation to execution. After understanding this case, the concept of closure should also be clear.

3. Decorator
Python supports decorator syntax. The concept of decorators is relatively obscure for beginners because it involves several concepts of functional programming (such as anonymous functions and closures). This is why this article introduces anonymous functions and closures first.

我们引用这篇文章对装饰器的定义:
A decorator is a function that takes a function object as an argument, and returns a function object as a return value.
从这个定义可知,装饰器本质上只是一个函数,它借助闭包的语法去修改一个函数(又称被装饰函数)的行为,即decorator其实是个闭包函数,该函数以被装饰函数名(这个函数名其实是一个函数对象的引用)作为入参,在闭包内修改被装饰函数的行为后,返回一个新的函数对象。
特别说明:decorator并非必须以函数形式出现,它可以是任何可被调用的对象,例如它也可以class形式出现,参见这篇文章给出的例子。
在定义好函数装饰器的前提下,当外部调用这个被装饰函数时,decorator的语法糖会由Python解释器解释为先执行装饰器函数,然后在装饰器返回的新函数对象上继续执行其余语句。
来个实例分析一下:

#!/bin/env python
#-*- encoding: utf-8 -*-
 
def wrapper(fn):
 def inner(n, m):
  n += 1
  print &#39;in inner: fn=%s, n=%s, m=%s&#39; % (fn.__name__, n, m)
  return fn(n, m) + 6 // 这里有return且返回值为int对象
 return inner
 
@wrapper
def foo(n, m):
 print &#39;in foo: n=%s, m=%s&#39; % (n, m)
 return n * m
 
print foo(2, 3)

上面的示例中,foo通过@wrapper语法糖声明它的装饰器是wrapper,在wrapper中,定义了嵌套的inner函数(该函数的参数列表必须与被装饰函数foo的参数列表保持一致),装饰器wrapper修改foo的行为后,返回inner(注意:由于inner的返回值是个int对象,故wrpper最终返回的也是个int对象)。
调用foo(2, 3)时,Python解释器先调用wrapper对foo做行为改写,然后返回int对象,不难推测,上述代码的执行结果如下:

in inner: fn=foo, n=3, m=3
in foo: n=3, m=3
foo(2, 3)=15

更多Python的几个高级语法概念浅析(lambda表达式闭包装饰器)相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn