学会使用装饰器之前,首先要明白什么是闭包函数

1.闭包

(1)什么是闭包?

  • 闭包:内部函数对外部函数作用域里变量的引用
  • 闭包函数必须满足两个条件:1.函数内部定义的函数 2.包含对外部作用域而非全局作用域的引用

这个概念略微有一点官方,不太好理解,接下来我们用示例来说明:

示例一:以下仅仅在函数内部定义了一个函数,但并非闭包函数.

示例二:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是


在回头来看看对闭包函数的定义,是不是两条都满足?聪明的你,一定发现不满足第二条.对,这里的变量x,是属于全局变量,而非外部作用于域的变量。再来看看下面例子:

示例三:显然,该例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。但是,一般情况下,我们都会给闭包函数返回一个值.这里先不说为什么.在接下来的内容中,你会看到这个返回值的用途.

(2)现在我们来抽象的定义一下闭包函数。它是函数和与其相关的引用环境组合而成的实体。在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起成为闭包。在上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么這个函数不能算得上是闭包函数。

那么怎么知道一个闭包函数有多少个外部引用变量呢?看看下面代码


结果返回两个对象,这表明,在inner内部,引用了两个外部局部变量。如果引用的是非局部变量,那么这里输出的为None.

闭包函数的特点:1.自带作用域 2.延迟计算

(3)那么闭包函数有什么作用呢?我们清楚的知道,闭包函数在定义时,一定会绑定一个外部环境。這个整体才能算的上是一个闭包函数,那么我们可以利用这个绑定特性,来完成某些特殊的功能。

示例: 根据传入的URL,来下载页面源码


有人可以会说,这个不满足闭包函数的条件啊!我没有引用非全局的外部变量啊。其实并非如此,给,我们之前说过,只要在函数内部的变量都属于函数。那么我在index(url),这个url也属于函数内部,只不过我们省略一步而已,所以上面那个函数也是闭包函数。

2、装饰器——实现装饰器的过程中需要用到闭包函数

(1)装饰器作用及功能

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码(即对修改是封闭的,对扩展是开放的),这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

<1>不能修改被装饰的函数的源代码
<2>不能修改被装饰的函数的调用方式
<3>满足1、2的情况下给程序增添功能

那么根据需求,同时满足了这三点原则,这才是我们的目的。因为,下面我们从解决这三点原则入手来理解装饰器。

等等,我要在需求之前先说装饰器的原则组成:

  • < 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

简单举例:

例一:

例二:


上下图等效

(2)深刻理解装饰器的工作过程

(3)无参的装饰器

现有如下代码,我们需要计算一下代码执行的时间。

import time, randomdef index():time.sleep(random.randrange(1, 5))print("welcome to index page")index()

首先要理解Time模块的用法

根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.


当然你也可以这么写

(4)有参的装饰器——同样是实现计算代码运行时间,我们再来做一遍

现在问题来了,我们如何接收参数呢?

我们以下图的代码为例,在其中做修改

用wrapper传递参数:

接受多个参数和关键字参数——(*args, ** kwargs)

import timedef decorator(func):def warpper(*args,**kwargs):print(time.time())func(*args,**kwargs)   ##此处接受的参数与wrapper一致
return warpper@decorator
def f1(func_name):print('This is a function ' + func_name)@decorator
def f2(func_name1,func_name2):print('This is a function ' + func_name1)print('This is a function ' + func_name2)@decorator
def f3(func_name1,func_name2,**kwargs):print('This is a function ' + func_name1)print('This is a function ' + func_name2)print(kwargs)f1('test')
f2('test1','test2')
f3('test1','test2',a=1,b=2,c='westos')

现在可以开始着手写代码了——装饰器实现一个函数计时器

我们将解决如下问题

问题1:被装饰的函数有返回值
问题2:如何保留被装饰函数的函数名和帮助信息文档

import time
import random
import string
import functoolsli = [random.choice(string.ascii_letters) for i in range(10)]
print(li)def decorator(fun):"""这是一个装饰器"""@functools.wraps(fun) ##这的参数要和装饰器的参数一致,没有这一行打印的是默认的文档,# 就是wrapper函数的帮助文档def wrapper(*args,**kwargs):"""这是一个wrapper函数"""t1 = time.time()f = fun(*args,**kwargs)t2 = time.time()print('运行时间:%.8f' %(t2 - t1))return freturn wrapper@decorator
def con_add():s = ''for i in li:s += (i + ',')print(s)@decorator
def join_add():print(','.join(li))@decorator
def fun_list(n):"""这是fun_list函数"""return [i * 2 for i in range(n)]@decorator
def fun_map(n):return list(map(lambda x:x*2,range(n)))con_add()
join_add()
print(fun_list(10))
print(fun_map(10))
print(fun_list.__doc__)
print(fun_list.__name__)

对于有参的装饰器,如果上面的示例你还不明白,没事,我们再来一例!! ??

现在有这样的函数,我们需要写一个装饰器,完成以下功能:

当调用man函数时,打印:好好上班,你不用生娃,但必须做家务!
当调用woman函数时,打印:好好上班,你要生娃,家务男人去做!


(5)多个装饰器

现有如下代码:猜猜它们打印的顺序

def decorator_a(fun):print('Get in decorator_a')def inner_a(*args, **kwargs):print('Get in inner_a')res = fun(*args, **kwargs)return resreturn inner_a
def decorator_b(fun):print('Get in decorator_b')def inner_b(*args, **kwargs):print('Get in inner_b')res = fun(*args, **kwargs)return resreturn inner_b@decorator_a
@decorator_b
def f(x):
print('Get in f')
return x * 2
f(2)

结果:

3.使用装饰器过程中出现的问题解决

问题1:被修饰的函数有返回值

被修饰的函数有返回值,需要在装饰器中将函数的返回值传给一个参数,并返回这个参数,否则返回值为None

比如你这么写:在被装饰函数中有返回(x + y),在装饰器中我们将被装饰函数的计算结果赋值给res,那么如果你不返回,会怎么样呢?

问题二:保留被装饰函数的函数名和帮助信息文档

<1> 导入functools包,加入@functools.wraps(fun)
<2> 帮助信息的写法:""“帮助信息”""
<3> print(函数名.doc) ##帮助信息文档
<4>print(函数名.name) ##函数名

例:

4.装饰器练习题

题1:

编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的
每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一>个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每
一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型

import functools
def required_types(*kinds):def required(fun):@functools.wraps(fun)def wrapper(*args,**kwargs):for i in args:if not isinstance(i,kinds):print('TypeError:参数必须为',kinds)break#raise TypeError('参数必须为%s,%s' %kinds)else:res  = fun(*args,**kwargs)return resreturn wrapperreturn required@required_types(str,int)
def add(a,b):return a + b
print(add(1.5,2.0))

题2:inspect.getcallargs的使用

import functools
import inspectdef is_admin(fun):@functools.wraps(fun)def wrapper(*args,**kwargs):#inspect.getcallargs返回一个字典,key值是形参,value值#是对应的实参{'name':'root'}inspect_res = inspect.getcallargs(fun,*args,*kwargs)print('inspect的返回值: %s' %inspect_res)if inspect_res.get('name') == 'root':res = fun(*args,**kwargs)return reselse:print('not root user!')return wrapper@is_admin
def add_user(name):print('添加用户信息...')def del_user(name):print('删除用户信息...')add_user('root')

题3:

定义装饰器admin:判断用户是否登陆成功
定义装饰器login:判断用户是否为超级用户root
先判断用户是否登陆成功,再判断书不是root用户

import functools
import inspectdef is_admin(fun):@functools.wraps(fun)def wrapper(*args,**kwargs):#inspect.getcallargs返回一个字典,key值是形参,value值#是对应的实参{'name':'root'}inspect_res = inspect.getcallargs(fun,*args,*kwargs)print('inspect的返回值: %s' %inspect_res)if inspect_res.get('name') == 'root':res = fun(*args,**kwargs)return reselse:print('not root user!')return wrapperlogin_session = ['root', 'redhat', 'westos']def is_login(fun):@functools.wraps(fun)def wrapper(*args,**kwargs):if args[0] in login_session:res = fun(*args,**kwargs)return reselse:print('Error:%s未登录' %args[0])return wrapper@is_login
@is_admin
def add_student(name):print('添加学生信息...')add_student('linux')

Python之闭包、装饰器及相关习题练习相关推荐

  1. python高级-闭包-装饰器

    闭包内容: 匿名函数:能够完成简单的功能,传递这个函数的引用,只有功能 普通函数:能够完成复杂的功能,传递这个函数的引用,只有功能 闭包:能够完成较为复杂的功能,传递这个闭包中的函数以及数据,因此传递 ...

  2. SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理

    SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理 本文连接 简介 SIGIA_4P 网址 a. 课程OKR Objectives and ...

  3. python高阶函数闭包装饰器_Python_基础_(装饰器,*args,**kwargs,高阶函数,函数闭包,函数嵌套)...

    一,装饰器 装饰器:本质就是函数,功能是为其它的函数动态添加附加的功能 原则:对修改关闭对扩展开放 1.不修改被修饰函数的源代码 2.不修改被修改函数的调用方式 装饰器实现的知识储备:高阶函数,函数嵌 ...

  4. 初学者python笔记(装饰器、高阶函数、闭包)

    一个函数被定义完成后,甚至程序发布后,后期可能需要添加某些功能,但是我们不可能每次都去修改原函数的代码,这时候装饰器就可以上场了,本篇文章将会用一个个可实现的代码,由浅入深.循序渐进得阐述装饰器的强大 ...

  5. python入门day11闭包装饰器

    目录 闭包 例子 同级闭包 装饰器引入 装饰器使用 无参例子 有参例子 可变参数例子 带关键字参数的装饰器 双层装饰器 装饰器带参数 装饰器的应用 闭包 def func():a=100def inn ...

  6. python装饰器原理-python 中的装饰器及其原理

    装饰器模式 此前的文章中我们介绍过装饰器模式: 装饰器模式中具体的 Decorator 实现类通过将对组建的请求转发给被装饰的对象,并在转发前后执行一些额外的动作来修改原有的部分行为,实现增强 Com ...

  7. python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上

    Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...

  8. python中的装饰器(基础装饰器)

    文章目录 一 前置知识-高阶函数,闭包 1. 高阶函数 2. 闭包 二 函数装饰器 1. 什么是装饰器(原理)? 2. 装饰器的实现 3. 何时执行装饰器 4. wraps方法 三 类装饰器 一 前置 ...

  9. python装饰器类-PYTHON里的装饰器能装饰类吗

    扩展回答 如何理解python里的装饰器 通常可以理解它是一个hook 的回调函数. 或者是理解成python 留给二次开发的一个内置API. 一般是用回调和hook 方式实现的. 如何理解Pytho ...

最新文章

  1. Java 注册SIGINT信号,处理CTRL+C
  2. OpenCV GrabCut分割的实例(附完整代码)
  3. “问答官”活动SQL专场来啦!小米行李箱、无线鼠标等你拿!
  4. GridView隐藏列, 并能读取列值的解决方法(转载)
  5. Error:scalac: Error: scala.collection.immutable.$colon$colon.tl$1()Lscala/collection/immutable/List;
  6. 如何控制product search attribute支持的操作类型
  7. 用过C#的朋友可能认为它是一种十分安全的语言,其实C#也可以做到经典的缓冲区溢出。 本文章将用一个实例来描述C#究竟是如何发生缓冲区溢出的! 首先建立一个C# Console工程,并开启工程的“允许
  8. Servlet和JSP学习指导与实践(二):Session追踪
  9. socket抓包_64、抓包分析tcp与udp
  10. 魔幻阵matlab,MatLab入门手册
  11. 网关为0.0.0.0_距离ETH 2.0仅7天,目标价为?美元
  12. dbeaver 设置编码_DBeaver 一个神奇的数据库操作软件
  13. redis 失效时间单位是秒还是毫秒_redis分布式锁的这些坑,我怀疑你是假的开发...
  14. 把wasm反编译出来
  15. 微星笔记本win键失灵了怎么解决
  16. 基于html的2048小游戏,基于jQuery的2048小游戏设计(网页版)
  17. SteamVR简介(Yanlz+Steam+VR+Unity+AR+MR+XR+立钻哥哥+==)
  18. PHP架构师成长路线,PHP架构师要求
  19. 没想到,拼多多竟然想用AI种草莓给我吃
  20. 更换浏览器标题栏图标

热门文章

  1. seaborn可视化displot绘制直方图(histogram)并通过axvline函数在直方图中添加中位数(median)竖线(自定义中位数竖线的线条形式)
  2. R语言ggplot2可视化分组变量下的数据分布(线条、色彩配置)、WVPlots包的ShadowHist函数比较分组下的数据直方图、ggplot2分面图facet_wrap可视化分组变量下的数据分布
  3. R绘制散点图以及带圈定的散点图(Scatterplot With Encircling)
  4. R构建分位数回归模型(Quantile Regression)
  5. sklearn SVM(支持向量机)模型使用RandomSearchCV获取最优参数及可视化​​​​​​​
  6. 记忆网络RNN、LSTM与GRU
  7. 笔记本上的小键盘计算机怎样用,笔记本小键盘怎么开,详细教您笔记本小键盘怎么开启...
  8. 二值网络--XNOR-Net: ImageNet Classification Using Binary Convolutional Neural Networks
  9. 图像理解--Detecting and Recognizing Human-Object Interactions
  10. make 操作技巧指南--gcc版本设置