Python的闭包和装饰器
闭包
闭包(closure)是函数式编程的重要的语法结构,Python也支持这一特性,下面就开始介绍Python中的闭包。
首先看看闭包的概念:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,闭包是由函数和与其相关的引用环境组合而成的实体。
python函数作用域LEGB
不论在什么语言中,变量都是必不可少的,在python中,一般存在以下几个变量类型,local:函数内部作用域;enclosing:函数内部与内嵌函数之间;global:全局作用域;build-in:内置作用域。解释器在遇到变量的时候,会按照如下的顺序进行查找:L > E > G > B,简称LEGB。
Python中通过提供 namespace 来实现重名函数/方法、变量等信息的识别,其一共有三种 namespace,分别为:
local namespace: 作用范围为当前函数或者类方法
global namespace: 作用范围为当前模块
build-in namespace: 作用范围为所有模块
当函数/方法、变量等信息发生重名时,Python会按照 “local namespace -> global namespace -> build-in namespace”的顺序搜索用户所需元素,并且以第一个找到此元素的 namespace 为准。
简单闭包例子,打招呼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/26 20:38
# @File : Test4.py
"""
闭包的使用
"""
def outer(greet):
def inner(name):
print(greet,name)
return inner
out = outer('你好')
out('小明')
再看:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/26 20:38
# @File : Test4.py
"""
闭包的使用
"""
def outer(greet):
def inner(name):
print(greet,name)
return inner
out = outer('你好')
print (dir(out))
print (out.__closure__)
print (type(out.__closure__[0]))
print (out.__closure__[0].cell_contents)
输出:
E:\Python3\python.exe E:/Python工作空间/model/close/Test4.py
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
(<cell at 0x00000218DF1055E8: str object at 0x00000218DF100DF0>,)
<class 'cell'>
你好
Process finished with exit code 0
从这里可以看到闭包的原理,当内嵌函数引用了包含它的函数(enclosing function)中的变量后,这些变量会被保存在enclosing function的__closure__属性中,成为enclosing function本身的一部分;也就是说,这些变量的生命周期会和enclosing function一样。
Python中怎么创建闭包
在Python中创建一个闭包可以归结为以下三点:
闭包函数必须有内嵌函数
内嵌函数需要引用该嵌套函数上一级namespace中的变量
闭包函数必须返回内嵌函数
通过这三点,就可以创建一个闭包,是不是想到了上一篇中介绍的Python装饰器。没错,Python装饰器就是使用了闭包。
装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是为已经存在的函数或对象添加额外的功能
简单装饰器:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/26 16:17
# @File : Mydemo1.py
"""
简单装饰器
"""
import time
def func(func):
def say():
print('AOP')
func()
return say
@func
def Target():
print('start')
time.sleep(1)
print('end')
Target()
通过@语法糖把func函数进行嵌入
再看带参数的的函数:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/26 16:42
# @File : Mydemo2.py
"""
装饰器检验奇数偶数
"""
def check(func):
def wrapper(a,b):
res=func(a,b)
if res % 2 == 0:
print('偶数')
else:
print('奇数')
return wrapper
@check
def func(a,b):
res = a+b
return res
func(1,2)
check函数进行奇数偶数校验
从例子中可以看到,对于被装饰函数需要支持参数的情况,我们只要使装饰器的内嵌函数支持同样的签名即可。
带参数的装饰器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/26 19:51
# @File : Mydemo3.py
"""
带参数的装饰器
"""
def check(flag=True):
if flag:
def _check(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
if res % 2 == 0:
print('偶数')
else:
print('奇数')
return wrapper
else:
def _check(func):
return func
return _check
@check(True)
def func1(a,b):
print('执行func1')
return a+b
@check(False)
def func2(a,b):
print('执行func2')
return a+b
func1(9,10)
func2(9,10)