博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python的闭包和装饰器
阅读量:2441 次
发布时间:2019-05-10

本文共 3677 字,大约阅读时间需要 12 分钟。

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 innerout = 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 innerout = 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__'](
,)
你好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 timedef func(func):    def say():        print('AOP')        func()    return say@funcdef 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@checkdef func(a,b):    res = a+b    return  resfunc(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+bfunc1(9,10)func2(9,10)

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

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

转载地址:http://ftcqb.baihongyu.com/

你可能感兴趣的文章
Number toString()方法
查看>>
javascript运算符_JavaScript比较运算符
查看>>
BroadcastChannel API
查看>>
css float属性_CSS float属性和清除
查看>>
字符串tostring_字符串toString()方法
查看>>
字符串方法中会修改原字符串_字符串padStart()方法
查看>>
字符串中include_字符串include()方法
查看>>
字符串endsWith()方法
查看>>
number.isnan_Number isNaN()方法
查看>>
虚拟dom_虚拟DOM
查看>>
安卓录制视频不录制外界声音_我如何录制我的视频
查看>>
如何在纯JavaScript中等待DOM ready事件
查看>>
react测试组件_测试React组件
查看>>
vue组件引入scss变量_如何将SCSS与Vue.js单个文件组件一起使用
查看>>
node.js删除文件_如何使用Node.js删除文件
查看>>
怎么过去web项目路径_我过去所做的所有软件项目
查看>>
开发人员,学习营销
查看>>
node 导出csv文件_如何使用Node.js编写CSV文件
查看>>
JavaScript数组简介
查看>>
创建react应用程序_如何调试React应用程序
查看>>