一、闭包的介绍


闭包可以保存函数内的变量




当闭包执行完毕,外部函数的变量才释放。

# 闭包的作用:可以保存外部函数的变量
# 闭包的形成条件
# 1.函数嵌套
# 2.内部函数使用了外部函数的变量或者参数
# 3.外部函数返回内部函数。这个使用了外部函数变量的内部函数称为闭包# 1.函数嵌套
def func_out():  # 外部函数num1 = 10def func_inner(num2):  # 内部函数result = num1 + num2  # 2.内部函数使用外部函数的变量或者参数print("结果为:{}".format(result))return func_inner  # 3.外部函数返回内部函数,不能写func_inner()# 获取闭包对象
new_func = func_out()  # 这个new_func就是闭包,这里的new_func=func_inner
new_func(1)  # 执行闭包
new_func(10)输出:
结果为:11
结果为:20

二、闭包的使用

# 外部函数接收姓名
def config_name(name):
# 内部函数保存外部函数的参数,并且完成数据显示的组成def inner(msg):print(name + "说:" + msg)
# 外部函数返回内部函数return inner# 创建闭包实例对象
tom = config_name("tom")  # 闭包一旦创建完成,外部函数的变量或参数name就定了,以后就不用再传了
jerry = config_name("jerry")
# 执行闭包
tom("哥们,过来一下,我们一起玩耍")
# 执行tom()的时候就是执行inner,在执行inner的时候传了一个参数"哥们,过来一下,我们一起玩耍"给msg,在inner函数内部组织之前一直保存的name参数
jerry("明天可以吗?")输出:
tom说:哥们,过来一下,我们一起玩耍
jerry说:明天可以吗?

这里,通过tom = config_name("tom") 和 jerry = config_name("jerry"),实际上只用了一份代码就创建了两个实例,然后在调用tom()和jerry()的时候,print(name + "说:" + msg)也只是用了这一份代码,提高了代码复用性

三、修改闭包内使用的外部变量

简言之,闭包就是那个内部函数,修改闭包内使用的外部变量,就是在内部函数里修改外部变量。

在闭包内修改外部函数的变量,需使用nonlocal关键字。

def func_out():num1 = 10def func_inner():  # 1.函数嵌套num1 = 20  # 在闭包内修改外部函数的变量result = num1 + 10  # 2.内部函数使用外部函数变量print(result)return func_inner  # 3.返回内部函数new_func = func_out()  # 创建一个闭包实例
new_func()  # 调用闭包实例输出:30
def func_out():num1 = 10def func_inner():  # 1.函数嵌套num1 = 20  # 在闭包内修改外部函数的变量result = num1 + 10  # 2.内部函数使用外部函数变量print(result)print("修改前的外部变量:", num1)func_inner()print("修改后的外部变量:", num1)return func_inner  # 3.返回内部函数new_func = func_out()  # 创建一个闭包实例输出:
修改前的外部变量: 10
30
修改后的外部变量: 10

可见,在创建闭包的时候程序就已经运行了,并且在内部函数里不能直接修改外部函数的变量值

def func_out():num1 = 10def func_inner():  # 1.函数嵌套global num1num1 = 20  # 在闭包内修改外部函数的变量result = num1 + 10  # 2.内部函数使用外部函数变量print(result)print("修改前的外部变量:", num1)func_inner()print("修改后的外部变量:", num1)return func_inner  # 3.返回内部函数new_func = func_out()  # 创建一个闭包实例输出:
修改前的外部变量: 10
30
修改后的外部变量: 10

可见,global并不能修改外部函数的变量值(global是与全局变量关联的)

def func_out():num1 = 10def func_inner():  # 1.函数嵌套nonlocal num1num1 = 20  # 在闭包内修改外部函数的变量result = num1 + 10  # 2.内部函数使用外部函数变量print(result)print("修改前的外部变量:", num1)func_inner()print("修改后的外部变量:", num1)return func_inner  # 3.返回内部函数new_func = func_out()  # 创建一个闭包实例输出:
修改前的外部变量: 10
30
修改后的外部变量: 20

可见,nonlocal可以修改成功

小结:在闭包内修改外部函数的变量,需使用nonlocal关键字

.
.
.
.
.
.

四、装饰器

装饰器是一个函数,嵌套函数,本质上是一个闭包函数。

原函数如下,

# 装饰器的目的:对已有函数进行额外的功能扩展,装饰器本质上是一个闭包函数,也是函数嵌套
# 装饰器的特点:
# 1.不修改已有函数的源代码
# 2.不修改已有函数的调用方式
# 3.给已有函数添加额外的功能def comment():print("发表评论")comment()

需求:在“发表评论”前,验证是否已登录,装饰器写法:

# 装饰器的目的:对已有函数进行额外的功能扩展,装饰器本质上是一个闭包函数,也是函数嵌套
# 装饰器的特点:
# 1.不修改已有函数的源代码
# 2.不修改已有函数的调用方式
# 3.给已有函数添加额外的功能
def decorator(func):  # 如果闭包函数的参数有且只有一个并且是函数类型,那么这个闭包函数是装饰器def inner():print("已添加登录验证")func()  # 在内部函数里面对已有函数进行装饰,这里func()=comment()return innerdef comment():print("发表评论")comment = decorator(comment)  # 调用装饰器对已有函数进行装饰,comment=func,然后comment=inner
comment()  # 实际是执行inner()输出:
已添加登录验证
发表评论


# 装饰器的目的:对已有函数进行额外的功能扩展,装饰器本质上是一个闭包函数,也是函数嵌套
# 装饰器的特点:
# 1.不修改已有函数的源代码
# 2.不修改已有函数的调用方式
# 3.给已有函数添加额外的功能
def decorator(func):  # 如果闭包函数的参数有且只有一个并且是函数类型,那么这个闭包函数是装饰器print("装饰器执行了")def inner():print("已添加登录验证")func()  # 在内部函数里面对已有函数进行装饰,这里func()=comment()return inner# 装饰器语法糖写法:@装饰器名称。装饰器语法糖写法更简单
@decorator  # @装饰器名称,此处实际是封装了comment = decorator(comment),以后comment=inner
def comment():print("发表评论")comment()  # 实际是执行inner()# 装饰器的执行时间:当当前模块加载完成,装饰器会立即执行对已有函数进行装饰输出:
装饰器执行了
已添加登录验证
发表评论

1、@decorator等价于comment = decorator(comment)

2、装饰器的执行时间:当当前模块加载完成,装饰器会立即执行对已有函数进行装饰

3、命名上面文件名为mydecorator.py,当在别的文件中时

import mydecorator,模块加载完成,即立即执行装饰
mydecorator.comment(),这里实际是已装饰完成innter()函数

五、装饰器的使用

import timedef decorator(func):def inner():  # 内部函数对已有函数进行装饰begin = time.time()  # 获取距离1970-1-1-0:0:1的时间差func()  # func() = 最早的work()end = time.time()dur = end - beginprint("函数执行完成耗时:", dur)return inner@decorator  # work = decorator(work), work = inner
def work():for i in range(10000):print(i)work()  # 装饰完成,work = inner

六、通用的装饰器

通用的装饰器,这个装饰器可以装饰任意函数

规则:使用装饰器装饰已有函数的时候,内部函数的类型和被装饰函数的类型要保持一致
被装饰的函数有参数,内部函数也要给参数;
被装饰的函数有n个参数,内部函数也要给n个参数;
被装饰的函数有返回值,内部函数也要有返回值

# 通用的装饰器:可以装饰任意类型的函数
# -------装饰有参数的函数----------
def decorator(func):def inner(a, b):print("正在努力执行加法计算")func(a, b)return inner#用装饰器语法糖方式装饰带参数的函数
@decorator  # 等价于 add_num = decorator(add_num), add_num = inner
def add_num(num1, num2):result = num1 + num2print("结果为:", result)add_num(1, 2)  # 即执行inner输出:
正在努力执行加法计算
结果为: 3
# 通用的装饰器:可以装饰任意类型的函数
# -------装饰有参数和有返回值的函数----------
def decorator(func):def inner(a, b):print("正在努力执行加法计算")num = func(a, b)return numreturn inner#用装饰器语法糖方式装饰带参数的函数
@decorator  # 等价于 add_num = decorator(add_num), add_num = inner
def add_num(num1, num2):result = num1 + num2return resultresult = add_num(1, 2)  # 即执行inner
print("结果为:", result)输出:
正在努力执行加法计算
结果为: 3

通用的装饰器:

# 通用的装饰器:可以装饰任意类型的函数
# -------装饰有不定长参数和有返回值的函数----------
def decorator(func):def inner(*args, **kwargs):print("正在努力执行加法计算")# *args,把元祖里的每一个元素按位置参数传参# **kwargs,把字典里的每一个键值对按关键字方式传参# 这里对元祖和字典进行拆包,仅限于被装饰函数为不定长参数的函数使用num = func(*args, **kwargs)return numreturn inner#用装饰器语法糖方式装饰带参数的函数
@decorator  # 等价于 add_num = decorator(add_num), add_num = inner
def add_num(*args, **kwargs):result = 0# *args;元组类型;**kwargs:字典类型for value in args:result += valuefor value in kwargs.values():result += valuereturn resultresult = add_num(1, 2)  # 即执行inner
print("结果为:", result)输出:
正在努力执行加法计算
结果为: 3

通用装饰器的使用:

# 通用的装饰器:可以装饰任意类型的函数
# -------装饰有不定长参数和有返回值的函数----------
def decorator(func):def inner(*args, **kwargs):print("正在努力执行加法计算")# *args,把元祖里的每一个元素按位置参数传参# **kwargs,把字典里的每一个键值对按关键字方式传参# 这里对元祖和字典进行拆包,仅限于被装饰函数为不定长参数的函数使用num = func(*args, **kwargs)return numreturn inner#用装饰器语法糖方式装饰带参数的函数
@decorator  # 等价于 add_num = decorator(add_num), add_num = inner
def add_num(*args, **kwargs):return "哈哈"result = add_num()  # 即执行inner
print("结果为:", result)输出:
正在努力执行加法计算
结果为: 哈哈

七、多个装饰器的使用

# 定义装饰器
def make_p(func):def inner():# 在内部函数对已有函数进行装饰result = "<p>" + func() + "</p>"  # func = contentreturn resultreturn inner@make_p  # content = make_p(content), content = inner
def content():return "人生苦短,我用Python!"result = content()  # content = inner
print(result)输出:
<p>人生苦短,我用Python!</p>
# 定义装饰器
def make_p(func):print("make_p装饰器执行了")def inner():# 在内部函数对已有函数进行装饰result = "<p>" + func() + "</p>"  # func = contentreturn resultreturn innerdef make_div(func):print("make_div装饰器执行了")def inner():# 在内部函数对已有函数进行装饰result = "<div>" + func() + "</div>"  # func = contentreturn resultreturn inner@make_div
@make_p  # content = make_p(content), content = inner
def content():return "人生苦短,我用Python!"result = content()  # content = inner
print(result)输出:
make_p装饰器执行了
make_div装饰器执行了
<div><p>人生苦短,我用Python!</p></div>

小结:

多个装饰器的过程:由内到外装饰,先执行内部的装饰器,再执行外部的装饰器
content = make_div(make_p(content))

分步拆解:
1、content = make_p(content),内部装饰器装饰完成,content = make_p.inner
2、content = make_div(make_p.inner)

八、带参数的装饰器

def decorator(func):  # 装饰器只能接收一个参数并且是函数类型def inner(a, b):print("正在执行加法计算")func(a, b)return inner@decorator
def add_num(a, b):result = a + bprint(result)@decorator
def substract_num(a, b):result = a - bprint(result)add_num(1, 2)
substract_num(1, 3)输出:
正在执行加法计算
3
正在执行加法计算  # 这句话是加法计算,不是我们想要的
-2

改进如下,

def return_decorator(flag):  # return_decorator是一个普通函数def decorator(func):  # 装饰器只能接收一个参数并且是函数类型def inner(a, b):if flag == "+":print("正在执行加法计算")elif flag == "-":print("正在执行减法计算")func(a, b)return innerreturn decorator  # 当调用函数的时候返回一个装饰器decorator# return_decorator("+")函数返回decorator,→ @decorator => add_num = decorator(add_num)
@return_decorator("+")
def add_num(a, b):result = a + bprint(result)@return_decorator("-")
def substract_num(a, b):result = a - bprint(result)add_num(1, 2)
substract_num(1, 3)输出:
正在执行加法计算
3
正在执行减法计算
-2

1、装饰器只能接收一个参数并且是函数类型
2、带参数的装饰器,其实就是在装饰器外面套了一个普通函数,让函数接收参数,在函数内部返回的是一个装饰器,再用装饰器去装饰函数

九、类装饰器

# 类装饰器,使用类装饰已有函数
class MyDecorator:def __init__(self, func):self.func = func  # func = show# 实现__call__()方法,让对象变成可调用对象,可调用的对象能够像函数使用def __call__(self, *args, **kwargs):print("课已讲完")self.func()@MyDecorator  # @MyDecorator => show = MyDecorator(show)
def show():print("快下课了")show()  # 执行show()→执行MyDecorator类创建类实例对象,show()=>对象()
print(show)输出:
课已讲完
快下课了
<__main__.MyDecorator object at 0x000002BF218D8048>

拓展:函数能够被调用,是因为函数自带__call__方法

def test():print("哈哈")print(dir(test))  # dir(),返回函数内部的属性和方法,返回一个列表输出:
['__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__']

十、装饰器方式添加路由

urlpatterns = [path('admin/', admin.site.urls),path("index/", views.index),path("login/", views.myview.as_view())]

示例,index函数已开发完毕,现在要在index的基础上增加新的功能,可以使用装饰器,因为需要同时告知router,因此是带参数的装饰器。

def route(path):def decorator(func):  # 装饰器要装饰函数,所以这里要有func形参并且是函数类型# 当执行装饰器的时候就需要把路由添加到路由列表里,相当于此时已经urlpatterns.append(route, view.index)urlpatterns = []urlpatterns.append((path, func))def inner(request):print("这是装饰器方式添加的路由")result = func(request)return resultreturn innerreturn decorator@route("index/")  # 等价于@decorator,接着index = decorator(index),接着index = inner
def index(request):  # 视图函数负责处理业务逻辑,request对象封装所有请求信息print(request)return HttpResponse("我的首页呀,哈哈")class myview(View):def get(self, request):return HttpResponse("这是基于类的视图实现方式")

PS: source, bilibili

Python:闭包(简介、使用方法、nonlocal修改闭包内使用的外部变量)、装饰器(定义、作用、通用装饰器、多个装饰器、带参数的装饰器、类装饰器、装饰器方式添加WEB框架的路由)相关推荐

  1. 修改闭包内使用的外部变量

    1. 修改闭包内使用的外部变量 修改闭包内使用的外部变量的错误示例: # 定义一个外部函数 def func_out(num1):# 定义一个内部函数def func_inner(num2):# 这里 ...

  2. Django基础-Web框架-URL路由

    Django基础-Web框架-URL路由 一.Django基础–Web框架 MVC和MTV框架 MVC 把Web应用分为模型(M).视图(V).控制器(C)三层,他们之间以一种插件式的,松耦合的方式联 ...

  3. IDEA中Java项目删除Web框架后无法再次添加Web框架解决办法

    1.首先在IntelliJ IDEA 2021.2版本下添加Web框架 (1)鼠标右击项目Suke-->选择:Add Framework Support... (2)勾选Web Applicat ...

  4. 实现父类一个动物的类, 包括成员变量名字年龄皮毛颜色,带参数构造函数,动物类有一个方法,move,打印动物是可以动的 1.《实现一个子类老鼠的类,继承动物类,老鼠类继承父类成员变量,老鼠还有个自己的属

    编写一个程序,程序包括如下内容 实现父类一个动物的类, 包括成员变量名字年龄皮毛颜色,带参数构造函数,动物类有一个方法,move,打印动物是可以动的 1.<实现一个子类老鼠的类,继承动物类,老鼠 ...

  5. gin 项目结构_Go Web 框架 Gin 路由的学习

    Gin 是目前应用比较广泛的Golang web 框架.目前,Github Star 数已经达到了3.8w. 框架的实现非常简单,可定制性非常强,性能也比较好,深受golang开发者的喜爱.Gin 提 ...

  6. Go Gin web框架的路由原理及中间件原理

    一.Gin框架的路由原理: 参考: go路由httprouter中的压缩字典树算法图解及c++实现 Golang-gin框架路由原理 首先了解下什么是路由? 简而言之,http路由即是一条http请求 ...

  7. Go语言WEB框架:路由注册

    路由注册 路由注册用于建立URL路径与处理器函数(也可以叫控制器函数)的对应关系.一条路由规则由三部分组成: http请求方法 url路径 处理器函数 以下代码注册了一个处理器函数:Hello,当用户 ...

  8. Python使用三种方法批量修改记事本文件编码格式

    应用背景:近期计划写一个贝叶斯算法邮件分类的教学案例,苦于没有足够的训练集,就让同学们帮忙每人从自己的邮箱中找几封垃圾邮件把内容复制下来放到记事本文件中发给我,但是忘了提前统一编码格式要求,所以收到的 ...

  9. delphi 异步 调用 带参数_Dubbo 关于同步/异步调用的几种方式

    我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制:基于这种机制,Dubbo 实现了以下几种调用方式: 同步调用 异步调用 参数回调 事件通知 同步调用 同 ...

最新文章

  1. 转:使用 PHP 直接在共享内存中存储数据集
  2. Go语言范围(Range)
  3. 六、Webpack详解学习笔记——webpack的安装、起步、配置、loader的使用、webpack中配置Vue、plugin的使用、搭建本地服务器、webpack配置的分离
  4. 自考计算机非笔试英语怎么考,自考中的非笔试课程是什么,怎么进行考核?
  5. 搜索场景下的智能实体推荐
  6. 开发者论坛一周精粹(第十四期):CVE-2017-7529:Nginx敏感信息泄露
  7. mac+ffmpeg+php,mac折腾安装ffmpeg小记
  8. 雪花算法生成主键id
  9. 数据库加密乱码_加密数据库中的密码
  10. 将word文档转换为图片格式的PDF
  11. 嵌入式中的 C 语言
  12. 计算机无法连接共享打印机,共享打印机无法连接,小编教你共享打印机无法连接怎么办...
  13. 股票自动买卖 java_股票如何实现程序化交易和自动交易?
  14. python数据分析简历_帮粉丝推荐简历|Python数据分析师
  15. 安焦删除贴 牛人纷纷出现(2)
  16. 解决adb shell root权限
  17. Multisim仿真 错误Error: Shorted voltage sources found: Vfgen_src_negative
  18. Antlr Tool与antlr runtime的版本一致性问题
  19. 人生是什么?感悟3:工作不是生活的全部
  20. echarts 地图上边画柱状图

热门文章

  1. Python中内置函数的介绍
  2. MSSQL数据库中发现D99_Tmp数据表的处理办法
  3. android 设备注册,i2c_设备注册流程
  4. linux下intel无线网卡安装失败,ubuntu 16.04无法安装无线网卡驱动
  5. java maven 读取配置文件_Java项目和maven项目中如何获取设置配置文件中的属性
  6. ue编辑器漏洞_编辑器漏洞手册
  7. 43大学计算机信息技术查询,大学计算机信息技术教程
  8. 感恩节日海报设计模板|简洁优雅的花卉主题海报
  9. APP设计UI优秀案例|价格标签这样设计才更直观!
  10. 清新手绘水果平面设计|面膜的包装设计越来越精致了!