python 小兵(8)闭包和装饰器
闭包”是什么,以及,更重要的是,写“闭包”有什么用处。
(个人理解)
1、“闭包”是什么
首先给出闭包函数的必要条件:
- 闭包函数必须返回一个函数对象
- 闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量),而返回的那个函数内部不一定要return
- 是的,闭包在被返回时,它的所有变量就已经固定,形成了一个封闭的对象,这个对象包含了其引用的所有外部、内部变量和表达式。当然,闭包的参数例外
首先,明确函数的作用域问题:
(1)外层函数f1可以调用内层函数f2,但无法引用f2内部的变量x
(2)内层函数f2可以引用外层函数f1的变量y
def f1(y):
def f2(x):
return x+y #内层函数f2可以引用外层函数f1的变量y
return f2# return f2() 区别??外层函数f1可以调用内层函数f2
#return x 但不能引用f2内部的变量x,这样写就错了
(3)如果把全局当做根函数(我自己提出的一个概念),那么就有:
全局之于f1,等效于f1之于f2
理解三个层次,从外到内分别是:global-f1-f2
也就是说,在全局可以调用f1,但无法引用f1内部的y;f1可以引用全局变量x
x=5
def f1(y):
return x+y #f1可以引用全局变量x
f1(1)#全局可以调用f1
#print(y)但无法引用f1内部的y,这样写就错了
(4)在外层函数里调用内层的变量,how to?
比如,想在global里调用f1内部的变量,怎么办?
思路:只能很曲折地。本来在global只能调用到f1,而不能调用f1内部的变量。
于是只能让调用f1的时候,f1就把内部的变量主动交出来,也就是return 想要访问的变量。
def f1():
y=1
return y
foo=f1()
以上就是在全局调用了f1(),f1返回就是y,就这样比较曲折地,全局的foo还是拿到了f1内部的变量y的值。
以上是拿变量,把变量改成函数,也就是在global里想拿到f1内部定义的函数,思路是一样的:
本来在全局是拿不到f1内部的任何东西,但只要f1把内部的东西返回,就可以通过在全局调用f1,来拿到f1内部的东西。
def f1():
def f2(x):
return x
return f2
g=f1()
g(5)
(5)闭包来了
以上,通过f1主动把f2返回,全局的g拿到了本来拿不到的f1作用域内部的f2函数,这样相当于就可以在全局调用f2了。不过,这还不是闭包。因为这时,你在全局拿到的f2,就是f2本身,跟f1可以说没有关系了。你在执行g=f1()完毕后,f1就被回收了,变量g指向函数f2。后来的g(5)就是在调用f2这个函数,传入参数5,执行。
闭包是更纠结一点的情况:在全局拿到f2,且这个f2还跟f1还有关系。
先明确一下,函数内的变量和函数定义时的形参,作用域完全相同。
def f1(y):
#y'=5 想说y'和y的作用域完全相同,只不过y'作为变量取值是固定的,y作为形参取值由传入实参决定,是可变的。但这里我们讨论的是作用域而不是取值
def f2(x):
#x'=1 x'和x的作用域也是完全相同的
return x+y
return f2
因此为了简单说明问题,我们统一写作函数内定义变量。
def f1():
y=5
def f2():
x=1
return x+y
return f2
g=f1()
g()
这里有三个层次,从外到内分别是:global-f1-f2
其中,
global作用域里有变量g,和函数f1
f1的作用域里有变量y,和函数f2
f2的作用域里有变量x
根据上面的规则,内层可以引用外层的变量,所以f2这一层里虽然没有变量y,但是引用了f1这一层的y,这个是符合规则的。
因为解释器知道f2依赖于变量y,因此,g=f1()执行后,f1和变量y还不会被回收,但是又不是作用域f2里面的东西,成为“自由变量”。
此时,g就是一个闭包,因为它符合闭包是"引用了自由变量的函数的定义"。g这个闭包包含f2的定义,而f2引用了自由变量y
不过这种写法没有价值,因为g()的值永远是6。
一般的用法是g()得是变量,比如实现计数器
def hellocounter ():
count=[0]
def counter():
count[0]+=1
print('Hello,'',',count[0],' access!')
return counter
hello=hellocounter()
hello()
hello()
hello()
#输出是1 2 3
#hello是闭包函数,自由变量是count,因为它不是counter这个函数内部的变量,但是却被counter引用。
闭包一定要有自由变量,这个自由变量产生有两个条件:一是内层函数去引用了外层函数的变量,二是内层函数被调用时,外层函数已经不见了,所以变量才会自由。
2、闭包的应用场景
1、保护函数内的变量安全。函数f1中y只有函数f2才能访问,而无法通过其他途径访问到,因此保护了y的安全性。
2、在内存中维持一个变量。依然如前例,由于闭包,函数f1中y的一直存在于内存中,因此每次执行c(),都会给y自加1。
3、闭包会在父函数外部,改变父函数内部变量的值。也就是,在f1的外部,通过g改变f1内部的变量/函数f2的值。
所以,如果把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
也就是如果把f1看成一个对象,把g看做对象的公有方法,f2看做对象的私有属性,那么这时相当于可以做到:通过公有方法改变私有属性,一般是不建议的,违反了类的封装性,使用要小心。
3.装饰器
本质就是闭包
import time
def inner(func):
def wapeer(*args,**kwargs):
s_time=time.time()
func(*args,**kwargs)
ss_time=time.time()
print(ss_time-s_time)
return wapeer
@inner#python的语法糖
def num_sum(*args,**kwargs):
print(*args)
num_sum(1,4,35,6,7,8)
@inner =num_sum=inner(num_sum)
上面的就是装饰器简单的说就是所有方法都可以用装饰器一个方法都可以调用
一个方法可以用多个装饰器
有参装饰器
装饰器本身是用来是为一个函数是实现新的功能,并且不改变原函数的代码以及调用方式。
遇到这样一种问题:
众多函数调用了你写的装饰器,但客户有需求说,我想实现我可以随之控制装饰器是否生效。
那你就不可能在得到命令的时候去原函数头部去做删除和添加装饰器调用的命令。这是就可以用到带参数的装饰器,定义一个开关,调用装饰器的时候,把这个装饰器的开关参数给传递进去,这样当开关打开的时候装饰器生效,关闭的时候则只执行原函数的代码。
举例:开关参数为True的时候执行过程:

1 F = True #step 1 装饰器的开关变量
2 def outer(flag): #step 2
3 def wrapper(func): #step 4
4 def inner(*args,**kwargs): #stpe 6
5 if flag: #step 9
6 print('before') #step 10
7 ret = func(*args,**kwargs) #step 11 执行原函数
8 print('after') #step13
9 else:
10 ret = func(*args,**kwargs)
11 print('123')
12 return ret #step 14
13 return inner #step 7
14 return wrapper #step 5
15
16 @outer(F) #先执行step 3 :outer(True)这个函数,然后step 6:@wrapper #此处把开关参数传递给装饰器函数
17 def hahaha():
18 pass #step 12
19 hahaha() # step 8 相当于inner()

开关参数为False的时候执行过程:

1 F = False #stpe1 装饰器的开关变量
2 def outer(flag): #step 2
3 def wrapper(func): #step 4
4 def inner(*args,**kwargs): #stpe 6
5 if flag: #step 9
6 print('before')
7 ret = func(*args,**kwargs)
8 print('after')
9 else:
10 ret = func(*args,**kwargs) #step 10 执行原函数
11 print('123') #step 12
12 return ret #step 13
13 return inner #step 7
14 return wrapper #step 5
python 小兵(8)闭包和装饰器的更多相关文章
- python中的闭包和装饰器
重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...
- 21.python中的闭包和装饰器
python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...
- Python 中的闭包与装饰器
闭包(closure)是函数式编程的重要的语法结构.闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性. 如果在一个内嵌函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内嵌函数 ...
- 轻松理解python中的闭包和装饰器 (下)
在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...
- 轻松理解python中的闭包和装饰器(上)
继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
- python基础16_闭包_装饰器
不了解是否其他语言也有类似 python 装饰器这样的东西. 最近才发现ECMAScript6也是有生成器函数的,也有 yield generator 装饰器的基础知识是闭包: # 闭包:嵌套函数, ...
- 聊聊Python中的闭包和装饰器
1. 闭包 首先我们明确一下函数的引用,如下所示: def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函 ...
- 第十七篇 Python函数之闭包与装饰器
一. 装饰器 装饰器:可以拆解来看,器本质就是函数,装饰就是修饰的意思,所以装饰器的功能就是为其他函数添加附加功能. 装饰器的两个原则: 1. 不修改被修饰函数的源代码 2. 不修改被修饰函数的调用方 ...
- python中的闭包与装饰器
#原创,转载请留言联系 装饰器的本质就是闭包,所以想知道装饰器是什么,首先要理解一下什么是闭包. 闭包 1. 外部函数返回内部函数的引用.2. 内部函数使用外部函数的变量或者参数. def outer ...
随机推荐
- 【LeetCode】931. Minimum Falling Path Sum 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划 相似题目 参考资料 日期 题目地址:htt ...
- 一、SQL高级语句
摘抄别的博主的博客主要总去CSDN看不太方便自己整理一下加深记忆! 导入文件至数据库 #将脚本导入 source 加文件路径 mysql> source /backup/test.sql; se ...
- Redis的各种实现(Jedis、Redisson、Lettuce)
1.1.Jedis 是Redis的Java实现的客户端.支持基本的数据类型如:String.Hash.List.Set.Sorted Set. 特点:使用阻塞的I/O,方法调用同步,程序流需要等到so ...
- Ubuntu mininet+Ryu环境安装
我们使用下载Ryu源代码进行那个安装 Ryu官方文档:http://ryu.readthedocs.io/en/latest/ Ryu电子书:http://osrg.github.io/ryu/res ...
- AUGMIX : A SIMPLE DATA PROCESSING METHOD TO IMPROVE ROBUSTNESS AND UNCERTAINTY
目录 概 主要内容 实验的指标 Dan Hendrycks, Norman Mu,, et. al, AUGMIX : A SIMPLE DATA PROCESSING METHOD TO IMPRO ...
- 云南农职《JavaScript交互式网页设计》 综合机试试卷⑥——简易旅游网
本页面分为顶部导航.登录注册栏,中部图片展示.主体内容和底部反馈模板 一.导航栏部分 要求一:设置菜单栏(二级菜单)和登录注册模块 要求二:当鼠标悬停到菜单栏(一级菜单)时,二级菜单以滑动效果滑出显示 ...
- Java初学者作业——为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转。
返回本章节 返回作业目录 需求说明: 为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转. 实现思路: 定义mainMenu方法,用于显示主菜单. 主菜单主要负责显示4个选项,分别是 ...
- Java初学者作业——定义客户类(Customer),客户类的属性包括:姓名、年龄、电话、余额、账号和密码;方法包括:付款。
返回本章节 返回作业目录 需求说明: 定义客户类(Customer),客户类的属性包括:姓名.年龄.电话.余额.账号和密码:方法包括:付款. 实现思路: 定义 Customer 类,并添加姓名.余额. ...
- 编写Java程序,前方有 3km 的道路障碍,4 辆普通车不能通过,必须等到清障车完成作业离开后,才能继续行驶。用程序来模拟这一过程的发生
查看本章节 查看作业目录 需求说明: 前方有 3km 的道路障碍,4 辆普通车不能通过,必须等到清障车完成作业离开后,才能继续行驶.用程序来模拟这一过程的发生 实现思路: 创建清障车Wrecker类和 ...
- 编写Java程序,创建一个 Person 类,该类中有一个类成员变量 country、一个实例变量 name 和另一个实例变量 age。
返回本章节 返回作业目录 需求说明: 创建一个 Person 类,该类中有一个类成员变量 country.一个实例变量 name 和另一个实例变量 age. country 表示地区,name 表示姓 ...