您的位置:首页 > 编程语言 > Python开发

Python的闭包和装饰器

2017-07-26 20:55 537 查看


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)


对func1进行检验,func2则不检验


通过例子可以看到,如果装饰器本身需要支持参数,那么装饰器就需要多一层的内嵌函数

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: