闭包

由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。

给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。

例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。

比如大众推出了一款新车:小白轿车。

第一天价格为: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 averageravg = 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,这个就是闭包。

闭包的定义:

​ 1. 闭包是嵌套在函数中的函数。

​ 2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

如何判断判断闭包?举例让同学回答:

# 例一:
def wrapper():a = 1def 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_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)  # []

闭包的作用:保存局部信息不被销毁,保证数据的安全性。

闭包的应用

  1. 可以保存一些非全局变量但是不易被销毁、改变的数据。
  2. 装饰器。

闭包的解释:

闭包是存在嵌套函数当中的 内层对外层非全局变量的的引用 ,称之为自由变量 它不会随着函数的结束而消失,一直保存在内存,最终的目的是保证数据的安全

装饰器

开放封闭原则

软件面世时,不可能吧所有的功能都设计好,当前的未来一两年功能给你上线,定期更新迭代.对于软件之前写的源代码一般都不会修改,对函数里面的代码以及函数的调用方式.

开放原则: 在源码不改变的情况下,增加一些额外的功能

封闭原则: 不要改变源代码

python中装饰器: 完美诠释的开放封闭原则

装饰器 就是个函数 : 他要装饰一个函数,在不改变原函数以及调用方式的前提下,给其增加一个额外的功能

装饰器初识

# 1 李业,在一家xx科技有限公司工作,主管安排了一个任务,
# 写一个代码测试怼怼哥写的函数的执行效率。
# import time
# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')# print(time.time())
# start_time = time.time()
# index()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')# 2. 主管让你测试小邓,李大象,重复代码太多。
#
# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')
#
#
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')# start_time = time.time()
# func1()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')
#
# start_time = time.time()
# func2()
# end_time = time.time()
# print(f'此函数的执行效率{end_time-start_time}')# 3.  整合到函数中# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')
#
#
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')# def test_time(x):
#     start_time = time.time()
#     x()
#     end_time = time.time()
#     print(f'此函数的执行效率{end_time-start_time}')# test_time(func1)
# test_time(func2)# 4. 怼怼哥这个函数在实际项目中被500执行,主管要求:在被执行此函数时,
# 同时要测试一下被执行函数的效率。# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')
#
# # index()
# def test_time(x):
#     start_time = time.time()
#     x()
#     end_time = time.time()
#     print(f'此函数的执行效率{end_time-start_time}')
#
# test_time(index)# 版本4的问题: 开放原则满足了,封闭原则:不改变原函数的源码,以及调用方式。
# 违反了封闭原则:改变了函数的调用方式。# 版本5: 不能改变原函数的调用方式(闭包):# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')
#
# # index()
#
# # def func1():
# #     time.sleep(2)
# #     print('欢迎访问日记首页')
#
# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         x()
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#     return inner
#
# index = test_time(index)
# index()# 语法糖 @加上装饰器函数的名# def f():
#     print(666)
#
#
# f = '太白'
# print(f)# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         x()
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#     return inner
#
#
# # @test_time  # index = test_time(index)
# def index():
#     time.sleep(2)
#     print('欢迎访问博客园首页')# index = test_time(index)
# index()# def func1():
#     time.sleep(2)
#     print('欢迎访问日记首页')# @test_time
# def func2():
#     time.sleep(1)
#     print('欢迎访问评论首页')# func2 = test_time(func2)
# func3 = test_time(func3)
# func2()
'''100行代码'''
# index()'''10行代码'''
# index()'''50行代码'''
# index()# 版本6:被装饰函数有返回值# def test_time(x):  # x = index
#     def inner():
#         start_time = time.time()
#         ret = x()
#         # print(F'ret: {ret}')
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#         return ret
#     return inner
#
#
# @test_time  # index = test_time(index)
# def index():
#     time.sleep(0.5)
#     print('欢迎访问博客园首页')
#     return True
#
# print(index())  # inner()
# 你应该是让True返回给index()这样才完美了,但是现在index是inner,所以你要是完全不改变原函数的使用,
# 你print(index()) ---> True# 版本7: 被装饰函数带参数,无论加不加装饰器,你的实参'太白金星'应该传给形参n,。
# 但版本6不能实现传参,index('太白金星') ==  inner('太白金星')
#
# def test_time(x):  # x = index
#     def inner(*args,**kwargs):
#         # 函数的定义:* ** 聚合。
#         # args = ('苹果')
#         #args = (1, 3)
#         start_time = time.time()
#         ret = x(*args,**kwargs)
#         # 函数的执行:* ** 打散。
#         # ret = x(*('苹果'))  ==x('苹果',)
#         # ret = x(*(1, 3))  ==x(1,3)
#         # print(F'ret: {ret}')
#         end_time = time.time()
#         print(f'此函数的执行效率{end_time-start_time}')
#         return ret
#     return inner
#
#
# # @test_time  # index = test_time(index)
# def index(n):
#     time.sleep(0.5)
#     print(f'欢迎{n}访问博客园首页')
#     return True
#
# # @test_time  # index = test_time(index)
# def func2(a,b):
#     time.sleep(0.5)
#     print(f'最终结果:{a+b}')
#     return a + b
#
#
# print(index('苹果'))  # inner('苹果')
# print(func2(1,3)) # == inner(1,3)# def warpper(f):
#     def inner(*args,**kwargs):
#         '''被装饰函数之前的操作'''
#         # print(666)
#         ret = f(*args,**kwargs)
#         '''被装饰函数之后的操作'''
#         # print('执行完毕了')
#         return ret
#     return inner
#
# @warpper
# def func():
#     print(111)
# #
# func()
# func()
# func()
# func()
# func()# 装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能。
# 登陆认证,打印日志等。

带参数装饰器

def wrapper(f):def inner(*args,**kwargs):if f.__name__ == 'qq':ret = f(*args,**kwargs)return retelse:return innerdef wrapper(f):def inner(*args, **kwargs):ret = f(*args, **kwargs)return retreturn innerdef wrapper_out(n,*args,sex='男',):def wrapper(f):  # fdef inner(*args,**kwargs):ret = f(*args,**kwargs)  # func1()return retreturn innerreturn wrapperdef func1():print('in func1')
func = wrapper_out(1)  # wrapper函数名
ly = func(func1)  # wrapper(func1) = inner
ly()  # inner()def wrapper_out(n):def wrapper(f):def inner(*args,**kwargs):# if n == 'qq':#     username = input('请输入用户名:').strip()#     password = input('请输入密码:').strip()#     with open('qq',encoding='utf-8') as f1:#         for line in f1:#             user,pwd = line.strip().split('|')#             if username == user and password == pwd:#                 print('登陆成功')#                 ret = f(*args,**kwargs)#                 return ret#         return False# elif n == 'tiktok':#     username = input('请输入用户名:').strip()#     password = input('请输入密码:').strip()#     with open('tiktok', encoding='utf-8') as f1:#         for line in f1:#             user, pwd = line.strip().split('|')#             if username == user and password == pwd:#                 print('登陆成功')#                 ret = f(*args, **kwargs)#                 return ret#         return Falseusername = input('请输入用户名:').strip()password = input('请输入密码:').strip()with open(n,encoding='utf-8') as f1:for line in f1:user,pwd = line.strip().split('|')if username == user and password == pwd:print('登陆成功')ret = f(*args,**kwargs)return retreturn Falsereturn innerreturn wrapper
"""
# @wrapper_out('qq')
# def qq():
#     print('成功访问qq')
# qq()
# 看到带参数的装饰器分两步执行:
'''
@wrapper_out('腾讯')1. 执行wrapper_out('腾讯') 这个函数,把相应的参数'腾讯' 传给 n,并且得到返回值 wrapper函数名。2. 将@与wrapper结合,得到我们之前熟悉的标准版的装饰器按照装饰器的执行流程执行。
'''
"""@wrapper_out('qq')
def qq():print('成功访问qq')@wrapper_out('tiktok')
def tiktok():print('成功访问抖音')qq()
tiktok()
开发思路:增强耦合性

多个装饰器装饰一个函数

def wrapper1(func1):  # func1 = f原函数def inner1():print('wrapper1 ,before func')  # 2func1()print('wrapper1 ,after func')  # 4return inner1def wrapper2(func2):  # func2 == inner1def inner2():print('wrapper2 ,before func')  # 1func2()  # inner1print('wrapper2 ,after func')  # 5return inner2@wrapper2  # f = wrapper2(f) 里面的f == inner1  外面的f == inner2
@wrapper1  # f = wrapper1(f) 里面的f == func1  外面的 f == inner1
def f():print('in f')  # 3f()  # inner2()

转载于:https://www.cnblogs.com/Jacob-yang/p/11107496.html

Python之闭包与装饰器相关推荐

  1. python基础---闭包、装饰器

    闭包.装饰器 注意:使用装饰器,最终都会改变装饰函数的函数名称及所属属性,所以在实际的装饰器中如果后续会涉及用到装饰函数函数名或所属属性的,需要加入python自带的模块@wraps确保函数装饰后,不 ...

  2. Python 的闭包和装饰器

    翻译: TheLover_Z Part I 原文地址: http://blaag.haard.se/Python-Closures-and-Decorators–Pt–1/ 回想起来,当初我做出了错误 ...

  3. python 闭包_一篇文章读懂Python的闭包与装饰器!

    什么是装饰器? 装饰器(Decorator)相对简单,咱们先介绍它:"装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数", ...

  4. python提高——闭包、装饰器

    目录 1闭包 2装饰器 2.1装饰器功能 2.2示例 1无参数的函数 2被装饰的函数有参数 3被装饰的函数有不定长参数 4装饰器中的return 5装饰器带参数,在原有装饰器的基础上,设置外部变量 6 ...

  5. Python的闭包和装饰器

    什么是闭包 python中函数名是一个特殊的变量,它可以作为另一个函数的返回值,而闭包就是一个函数返回另一个函数后,其内部的局部变量还被另一个函数引用. 闭包的作用就是让一个变量能够常驻内存. def ...

  6. python的闭包及装饰器

    闭包: 闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外.所以,有另一种说 ...

  7. python装饰器与闭包_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。...

    转载:http://python.jobbole.com/81683/ 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需 ...

  8. python闭包和装饰器的区别_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。(转)...

    呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需要你了解一些函数式编程的概念,当然还有理解在python中定义和调用函数 ...

  9. Python之闭包、装饰器及相关习题练习

    学会使用装饰器之前,首先要明白什么是闭包函数 1.闭包 (1)什么是闭包? 闭包:内部函数对外部函数作用域里变量的引用 闭包函数必须满足两个条件:1.函数内部定义的函数 2.包含对外部作用域而非全局作 ...

最新文章

  1. Redis 笔记(02)— keys 键相关命令(查询数据库key数量、判断key是否存在、指定key过期时间、查看key类型、查看key剩余秒数、选择数据库、删除key、删除数据库)
  2. 收藏 | 计算机视觉中的Transformer
  3. 推荐10个小众简约,但是实用的软件和网站
  4. sqlalchemy根据表名动态创建model类
  5. java项目实现流水号自动增长
  6. golang var 初始化时机_你应该知道的 Go 调度器知识:Go 核心原理 — 协程调度时机...
  7. 前端学习(2174):打包文件的分析
  8. 【项目管理】项目管理计划
  9. 解决宿主机Ping不通虚拟机的问题
  10. Tomcat的结构概述
  11. linux下挂载U盘的方法
  12. L2-014. 列车调度-PAT团体程序设计天梯赛GPLT
  13. 个人财务管理系统mysql_开发个人财务管理系统(一)建数据库
  14. 频繁gc是什么意思_一次解决jvm GC过于频繁的经历
  15. 虚拟机的三种网络连接方式
  16. python selenium 爬虫遇到 由于目标计算机积极拒绝,无法连接
  17. 【中级软考—软件设计师】2操作系统2.6段页式存储【**】:2.6.1页式存储
  18. 浅谈程序环境和预处理
  19. Hadoop2.7下载安装
  20. 生日快乐程序_「秒福」小程序:送祝福,2020我们不一样

热门文章

  1. 24.Odoo产品分析 (三) – 人力资源板块(5) – 出勤(1)
  2. 为了转行程序员而努力的第四十四天-二叉树,MAC系统virtual box无法启动问题
  3. PS渐变自行车的做法
  4. 4.Python数据结构及算法----无序和有序列表:链表
  5. 关于技术博客--找工作--这些年--陆续的
  6. pythonqq交流群_使用 Python 获取 QQ 群投票数据
  7. JavaScript 实现图片上传前本地预览
  8. linux自动刷新桌面,Ubuntu下实现用Python开机自动更新壁纸为bing壁纸
  9. 激活函数ReLU、Leaky ReLU、PReLU和RReLU
  10. 微信公众平台PHP开发(转载)