python基础--闭包、装饰器
装饰器的详细使用
(1)小知识点补充
在这里我们先学一个简单的知识点。
li = ['alex', '银角', '女神', 'egon', '太白']
for i in enumerate(li):
print(i)
for index, name in enumerate(li, 1):
print((index, name))
for index, name in enumerate(li, 100): # 起始位置默认是0,可更改
print((index, name))
'''
输出的结果为:
(0, 'alex')
(1, '银角')
(2, '女神')
(3, 'egon')
(4, '太白')
(1, 'alex')
(2, '银角')
(3, '女神')
(4, 'egon')
(5, '太白')
(100, 'alex')
(101, '银角')
(102, '女神')
(103, 'egon')
(104, '太白')
'''
(2)闭包
下面讲解闭包这个概念是参考博客太白金星。希望大家有不懂的地方可以问我,可以共同讨论学习。
由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。
给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。
例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。
比如大众推出了一款新车:小白轿车。
第一天价格为:100000元,平均收盘价:100000元
第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元
第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元
........
series = []
def make_averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
print(make_averager(100000))
print(make_averager(110000))
print(make_averager(120000))
从上面的例子可以看出,基本上完成了我们的要求,但是这个代码相对来说是不安全的,因为你的这个series列表是一个全局变量,只要是全局作用域的任何地方,都可能对这个列表进行改变。
series = []
def make_averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
print(make_averager(100000))
print(make_averager(110000))
series.append(666) # 如果对数据进行相应改变,那么你的平均收盘价就会出现很大的问题,数据没有安全性。
print(make_averager(120000))
那么怎么办呢?有人说,你把他放在函数中不就行了,这样不就是局部变量了么?数据不就相对安全了么?
def make_averager(new_value):
series = []
series.append(new_value)
total = sum(series)
return total / len(series)
print(make_averager(100000)) # 100000.0
print(make_averager(110000)) # 110000.0
print(make_averager(120000)) # 120000.0
这样计算的结果是不正确的,那是因为执行函数,会开启一个临时的名称空间,随着函数的结束而消失,所以你每次执行函数的时候,都是重新创建这个列表,那么这怎么做呢?这种情况下,就需要用到我们讲的闭包了,我们用闭包的思想改一下这个代码。
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
avg = make_averager()
print(avg(100000))
print(avg(110000))
print(avg(120000))
大家仔细看一下这个代码,我是在函数中嵌套了一个函数。那么avg 这个变量接收的实际是averager函数名,也就是其对应的内存地址,我执行了三次avg 也就是执行了三次averager这个函数。那么此时你们有什么问题?
肯定有学生就会问,那么我的make_averager这个函数只是执行了一次,为什么series这个列表没有消失?反而还可以被调用三次呢?这个就是最关键的地方,也是闭包的精华所在。我给大家说一下这个原理,以图为证:
上面被红色方框框起来的区域就是闭包,被蓝色圈起来的那个变量应该是make_averager()函数的局部变量,它应该是随着make_averager()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,series变量就变成了一个叫自由变量的东西,averager函数的作用域会延伸到包含自由变量series的绑定。也就是说,每次我调用avg对应的averager函数 时,都可以引用到这个自用变量series,这个就是闭包。
闭包的定义:
闭包是嵌套在函数中的函数。
闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
如何判断判断闭包?举例让同学回答:
# 例一:
def wrapper():
a = 1
def inner():
print(a)
return inner
ret = wrapper()
# 例二:
a = 2
def wrapper():
def inner():
print(a)
return inner
ret = wrapper()
# 例三:
def wrapper(a,b):
def inner():
print(a)
print(b)
return inner
a = 2
b = 3
ret = wrapper(a,b)
以上三个例子,最难判断的是第三个,其实第三个也是闭包,如果我们每次去研究代码判断其是不是闭包,有一些不科学,或者过于麻烦了,那么有一些函数的属性是可以获取到此函数是否拥有自由变量的,如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了(了解):
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
avg = make_averager()
# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars) # ('series',)
当然还有一些参数,仅供了解:
# 函数名.__code__.co_freevars 查看函数的自由变量
print(avg.__code__.co_freevars) # ('series',)
# 函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames) # ('new_value', 'total')
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents) # []
闭包的作用:保存局部信息不被销毁,保证数据的安全性(这是一个十分重要的作用)。
闭包的应用:
- 可以保存一些非全局变量但是不易被销毁、改变的数据。
- 装饰器。
(3)装饰器
在使用装饰器之前,我们先开始了解一下开发封闭原则。那么什么是开发封闭原则呢?
# 开放封闭原则
# 开放:对代码的拓展是开放的。更新地图,加新枪,等等
# 封闭:对源码的修改是封闭的。闪躲用q。就是一个功能,一个函数。
# 别人用赤手空拳打你,用机枪扫你,扔雷.....这个功能不会改变。
1.对扩展是开放的
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2.对修改是封闭的
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对函数内部进行修改,或者修改了函数的调用方式,很有可能影响其他已经在使用该函数的用户。
所以装饰器最终最完美的定义就是:在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能。
那么装饰器是什么呢?
# 什么叫做装饰器
# 装饰器:装饰,装修,房子本来可以住,如果装修,不影响你住
# 而且体验更加,让你的生活中增加了许多的功能,例如看电视,洗澡
# 器:工具
# 装饰器:完全遵循开放封闭原则。
# 装饰器:在不改变原函数的代码以及调用方式的前提下,为其增加新的功能
(4)标准版装饰器
代码优化:语法糖
根据我的学习,我们知道了,如果想要各给一个函数加一个装饰器应该是这样:
def home(name,age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name,age)
print(f'欢迎访问{name}主页')
def timer(func): # func = home
def inner(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
return inner
home = timer(home)
home('太白',18)
如果你想给home加上装饰器,每次执行home之前你要写上一句:home = timer(home)这样你在执行home函数 home('太白',18) 才是真生的添加了额外的功能。但是每次写这一句也是很麻烦。所以,Python给我们提供了一个简化机制,用一个很简单的符号去代替这一句话。
def timer(func): # func = home
def inner(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')
return inner
@timer # home = timer(home)
def home(name,age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name,age)
print(f'欢迎访问{name}主页')
home('太白',18)
你看此时我调整了一下位置,你要是不把装饰器放在上面,timer是找不到的。home函数如果想要加上装饰器那么你就在home函数上面加上@home,就等同于那句话 home = timer(home)。这么做没有什么特殊意义,就是让其更简单化,比如你在影视片中见过野战军的作战时由于不方便说话,用一些简单的手势代表一些话语,就是这个意思。
至此标准版的装饰器就是这个样子:
def wrapper(func):
def inner(*args,**kwargs):
'''执行被装饰函数之前的操作'''
ret = func(*args,**kwargs)
'''执行被装饰函数之后的操作'''
return ret
return inner
这个就是标准的装饰器,完全符合代码开放封闭原则。这几行代码一定要背过,会用。
python基础--闭包、装饰器的更多相关文章
- python基础—函数装饰器
python基础-函数装饰器 1.什么是装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能. 装饰器的返回值是也是一个函数对象. 装饰器经常用于有切 ...
- 十. Python基础(10)--装饰器
十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...
- Day11 Python基础之装饰器(高级函数)(九)
在python中,装饰器.生成器和迭代器是特别重要的高级函数 https://www.cnblogs.com/yuanchenqi/articles/5830025.html 装饰器 1.如果说装 ...
- python函数闭包-装饰器-03
可调用对象 callable() # 可调用的(这个东西加括号可以执行特定的功能,类和函数) 可调用对象即 callable(对象) 返回为 True 的对象 x = 1 print(cal ...
- [python基础]关于装饰器
在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...
- 1.16 Python基础知识 - 装饰器初识
Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位 ...
- 【Python基础】装饰器的解释和用法
装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...
- 【Python】 闭包&装饰器
python中的函数本身就是对象,所以可以作为参数拿来传递.同时其允许函数的层级嵌套定义,使得灵活性大大增加. 闭包 闭包的定义:将函数的语句块与其运行所需要的环境打包到一起,得到的就是闭包对象.比如 ...
- python基础之 装饰器,内置函数
1.闭包回顾 在学习装饰器之前,可以先复习一下什么是闭包? 在嵌套函数内部的函数可以使用外部变量(非全局变量)叫做闭包! def wrapper(): money =10 def inner(num) ...
- python基础-----函数/装饰器
函数 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. 函数的优点之一是,可以将代码块与主程 ...
随机推荐
- HTML&CSS面试高频考点(一)
1. 行内元素/块级元素 非替换元素/替换元素 行内元素(内联元素):a, abbr(缩写), acronym(只取首字母缩写), b, bdo(文本方向), big, br, cite(引用), c ...
- JDK8--06:Stream流
一.描述 Stream流提供了筛选与切片.映射.排序.匹配与查找.归约.收集等功能 筛选与切片: filter:接收lambda,从流中排除某些元素 limit(n):截断流,使其元素不超过n ski ...
- 用JS实现改变文本框的只读属性
<input id="aaa" readonly><input id="bbb" readonly> <script>doc ...
- Javascript中的"函数是第一类对象(first-class object)"
本身这句话很好解释,函数有两个主要特点,援引自 陈新 译的<JavaScript模式>: 1.函数是第一类对象: 函数可以在运行时动态创建,还可以在程序执行过程中创建. 函数可以分配变量, ...
- 洛谷 P3916 【图的遍历】
这道题绿题有点高了吧... 我一开始的思路就是一个暴力的遍历,用递归加一个记忆化,对于一个点不断的往下搜索,然后确定最大的,返回,给上面的节点.就在这个过程中,我们是搜到最大的数然后返回给上层的数,那 ...
- Linux查看docker容器日志
docker logs -f 容器名或ID | grep fail | more grep fail:过滤包含fail的日志内容
- 使用SpringBoot构建REST服务-什么是REST服务
前言: 本文按照Spring官网构建REST服务的步骤测试,可以得到结论: 到底什么样的风格才是RESTful风格呢? 1,约束请求命令如下: GET,获取资源.例如:/employees表示获取列表 ...
- Spreading the Wealth,思维
题目去洛谷 题意: 很清晰,n个人,每人有一些硬币硬币总数sum≡0(mod n),通过一些互相交换,使硬币数平均(即每人有相同个数的硬币) 分析: 还是有点思维含量的,我们这样想,我们其实就是要确定 ...
- day07总结
元组常用操作 # ======================================基本使用======================================# 1.用途:元组就是 ...
- 面向对象之多态(Java实现)
本文借鉴于csdn,博客园,b站等各大知识分享平台 之前学习了封装与继承,封装就是数据的封装性(大致理解),继承就是一个类继承另一个类的属性,称为父子类 多态 多态是面向对象的第三大特性(共三大特性) ...