Python函数详解二(闭包、装饰器)
闭包其实利用了函数嵌套的概念,一般函数在内部定义一个变量,在外部由于作用域的关系是调用不到的,而闭包是将变量包起来,不管在哪里都可以调用的到。
函数的嵌套定义:函数内定义了另外一个函数
# 示例1
name='Alice'
def outer():
def inner():
print(name)
print(inner.__closure__)
return inner
其中outer称为外部函数,inner称为内部函数。
对于一个函数,outer是其函数名,outer()为函数的调用,python中函数名可以用做函数的参数也可以作为函数的返回值。
那么什么是闭包呢?
闭包满足的三个条件:
1. 必须是嵌套函数;
2. 内部函数必须引用外部函数中的变量
3. 外部函数返回了内部函数或者外部函数调用了内部函数
查看是否为闭包:__closure__
#示例2
def outer():
name = 'Alice'
def inner():
print(name)
print(inner.__closure__) #(<cell at 0x000001B4AEF3A0D8: str object at 0x000001B4AEF190F0>,)
return inner
可以试一下示例1的__closure__
值,值是None,所以示例1并不是闭包,原因是内部函数没有引用外部函数的变量。
内部函数的调用
f=outer() #调用外部函数,返回内部函数并存给f
f() #调用内部函数
注意: 内函数不能直接被调用。 闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,直到内部函数被调用结束。
闭包变量
局部变量的作用域在函数内部,如果内部函数想修改外部函数的变量,有两种方法:
1.将变量声明为nonlocal
,这表明变量不仅在函数内部生效,还在上一级函数中生效
def outer():
name = 'Alice'
def inner():
nonlocal name
name='Haha'
print(name)
return inner
f=outer()
f()
2.把闭包变量改成可变类型数据进行修改,比如列表。
def outer():
name = ['Alice']
def inner():
name=['Haha']
print(name[0])
return inner
f=outer()
f()
注意:
1.nonlocal
关键字只能用于嵌套函数中,并且外部函数中定义了相应的局部变量
2.外函数被调用一次返回了内函数,因此实际上闭包变量只有一份,以后每次调用内函数,都使用同一份闭包变量,一旦在内部函数修改了闭包变量,则这个闭包变量的值就已经修改了,不是最初的值了。
装饰器
装饰器本质上是一个函数,使用了闭包的特性,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
现有函数:
def sleep_f():
time.sleep(3)
如果要计算该函数的运行时间,代码如下
start_time=time.time()
sleep_f()
end_time=time.time()
print("程序运行了{0:.4f}秒".format(end_time-start_time))
缺点:
1.调用sleep_f()依旧无法打印运行时间。
2.如果还有其他很多函数要显示运行时间,同样的代码得写很多遍。
解决方法:使用装饰器。
定义装饰器如下:
def timer(func):
def inner(*args, **kwargs):
start_time=time.time()
func(*args, **kwargs)
end_time=time.time()
print("程序运行了{0:.4f}秒".format(end_time-start_time))
return inner
装饰器装饰函数的方法:函数的上一行加 @装饰器名,其中,@符号是装饰器的语法糖
装饰器使用的两种方法:
装饰器不带参数
# 被装饰函数不带参数
@timer
def sleep_f():
time.sleep(3)
#调用sleep_f,这时候就会打印运行时间了
sleep_f()
# 被装饰函数带参数
@timer
def add(a,b):
print(a+b)
time.sleep(2)
add(2,3)
装饰器带参数
timer
装饰器打印了函数的运行时间,如果还想打印日志,即想要装饰器含参数,需要在timer
的外层再装饰一层函数
def flog(name):
def timer(func):
def inner(*args, **kwargs):
print("调用{}模块的{}函数".format(name,inner.__name__))
start_time=time.time()
func(*args, **kwargs)
end_time=time.time()
print("程序运行了{0:.4f}秒".format(end_time-start_time))
return inner
return timer
@flog('module_a')
def add(a,b):
print(a+b)
time.sleep(2)
# 调用
add(6,7)
- 【Java提高十六】集合List接口详解
- JS的内建函数reduce
- Error: Cannot find module 'gulp-clone'问题的解决
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(四)代码简化
- Angular源码分析之$compile
- TensorFlow实例: 手写汉字识别
- iOS引入JavaScriptCore引擎框架(一)
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(三)搭建步骤
- iOS引入JavaScriptCore引擎框架(二)
- Java基础-03(01).总结运算符、键盘录入、if语句
- 使用git恢复未提交的误删数据
- Java web图片上传和文件上传
- 通过java程序模拟实现地铁票价2+2=12(r3笔记第94天)
- 由objC运行时所想到的。。。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 快速学习-Jenkins CLI自动补全
- 快速学习-Jenkins CLI计算节点
- mybatis的分页查询
- Cypress系列(50)- wrap() 命令详解
- 规则引擎在IoT的重要性?
- Js中String对象
- 如何让一个字符串执行?
- Array.apply(),new Array(),arr =[] 的区别
- 一文带你真正了解histroy
- 介绍一个可以离线查询 IP 来源和 ISP 信息的终端利器
- 这也太简单了吧!一个函数完成数据相关性热图计算和展示
- CentOS7安装Zookeeper
- CentOS7卸载OpenJDK,然后安装Oracle JDK
- Java停止线程的四种方法
- Educational Codeforces Round 95 (Rated for Div. 2) A-D