闭包

闭包是装饰器实现的底层部分,要想明白装饰器底层怎么实现,必须要搞懂闭包

闭包是什么?

将一个函数定义到一个函数内部,外函数的返回是内函数,这时这两个函数就构成了一个闭包

闭包举例:

def test(number):def test_in(number_in):print("in test_in 函数, number_in is %d" % number_in)return number + number_in#返回内函数tset_in的引用    return test_in# test函数返回一个函数test_in,所以这里t变量保存的是一个函数的引用
t = test(20)
# 使用指向test_in函数的引用t调用test_in函数
print(t(100))
# 使用指向test_in函数的引用t调用test_in函数
print(t(200))

闭包的优点及其应用

闭包的使用可以增加代码复用性,减少内存空间的开辟,如下例子:

#程序一
def creatNum(a,b,x):return a*x + ba=1
b=1
x=0
line1 = creatNum(a,b,x)
print(line1)                #line1是一个变量
x=1
line1 = creatNum(a,b,x)
print(line1)a=4
b=5
x=0
line2 = creatNum(a,b,x)
print(line2)                #line2是一个变量
x=1
line2 = creatNum(a,b,x)
print(line2)
#程序二
def line_conf(a, b):def line(x):return a*x + breturn lineline1 = line_conf(1, 1)     #line1是一个指向函数的引用
line2 = line_conf(4, 5)     #line2是一个指向函数的引用print(line1(0))
print(line1(1))print(line2(0))
print(line2(1))

以上两段程序的运行结果相同,但是程序二开辟了更少的内存空间,因为程序一每次调用函数都需要定义函数需要使用的变量,而程序二只需要一句line1 = line_conf(1, 1) 创建一个函数并使用变量保存其引用,则每次只需通过这个函数引用即可使用函数,不需要多次创建函数需要的参数,因此闭包减少了内存的开辟

装饰器

Python中的装饰器的主要思想类似设计模式中的装饰器模式(Decorator Pattern),简单的说就是对原来的方法进行功能增强

装饰器的定义和使用

装饰器使用场景比较常见,比如常见的站点后台操作,执行操作前一般需要进行权限的检验,检查用户是否登录,是否有进行该操作的权限等。如下例子,执行f1方法的操作前必须进行check_login方法(是否登录)的权限检验

def check_login():# 验证1# 验证2# 验证3pass
def f1():check_login()print('f1')
def f2():check_login()print('f2')
def f3():check_login()print('f3')
def f4():check_login()print('f4')

但是面例子的设计方式有一个非常大的缺陷,就是需要在方法的内部修改代码。还是上述例子,如果需要加上其他的检验操作,如是否有操作权限,限制条件是否满足等,就需要在f1等方法内部再加上一个权限检验函数,如果这样的验证操作很多,那么f1等方法的代码就需要不断地修改

一个好的程序是要遵守开放封闭原则的,封闭原则是指上述f1等方法在其功能完成后不应该再修改其内部代码,开放原则是指已完成的功能要能够方便的进行功能扩展同时不违背封闭原则。这时需要一个比上述方式更好的解决方案

def w1(func):def inner():# 验证1# 验证2# 验证3func()return inner
@w1
def f1():print('f1')
@w1
def f2():print('f2')
@w1
def f3():print('f3')
@w1
def f4():print('f4')

以上就是Python装饰器的使用,装饰器就是闭包和@w1

装饰器的底层原理

def w1(func):def inner():# 验证1# 验证2# 验证3func()return inner
@w1
def f1():print('f1')

上述程序,@w1是一个Python可以识别的特殊的符号,Python解释器在遇到@w1时将其解释为w1(f1),即把f1的引用作为参数传递给函数w1,并执行。w1是一个闭包方法,inner方法在w1方法执行后加载进内存(但是不会执行),f1为w1方法的参数,和inner方法一起加载进内存。这时内存中会有一个经过装饰的inner方法:


def inner():# 验证1# 验证2# 验证3f1()

接着Python解析器会将进行一个赋值,将上述这个经过修饰的inner方法的引用赋值给函数指针f1,即:

f1 = inner;#inner函数为:
def inner():# 验证1# 验证2# 验证3f1()

之后调用f1方法,其实就是调用内存中的一个在inner方法基础上进行f1方法装饰的方法

@w1
def f1():print('f1')

装饰器的各种形式

上面列举的都是无参装饰器,其实装饰器还可以是有参数并且有返回值的,以下是一个无参装饰器:

from time import ctime, sleepdef timefun(func):def wrappedfunc():print("%s called at %s"%(func.__name__, ctime()))func()return wrappedfunc#使用装饰器修饰foo函数
@timefun
def foo():print("I am foo")foo()
sleep(2)
foo()

1.使用装饰器装饰有参数的函数

当需要修饰的方法(foo方法)有参数时,装饰器就需要做相应的修改,闭包中的内部方法wrappedfunc方法需要添加和foo数量相同的参数,否则就不能使用闭包中的外部方法timefun修饰foo方法。原因就在装饰器的工作原理中:Python解析器将wrappedfunc方法增加了foo方法的功能生成的新方法(相当于增加了几句代码的wrappedfunc方法)的引用赋值给foo函数指针,完成这次赋值的前提是wrappedfunc方法的参数和foo方法的参数个数和参数类型必须相同

from time import ctime, sleep
def timefun(func):#为wrappedfunc添加参数,参数名可以和foo方法不相同,但是参数个数和参数类型必须和foo方法相同def wrappedfunc(a,b):print("%s called at %s"%(func.__name__, ctime()))#注意这里也要修改func(a,b)return wrappedfunc@timefun
def foo(a,b):print(a+b)foo(1,2)
sleep(2)

2.使用一个装饰器装饰有不同参数的多个函数

如果想用同一个装饰器修饰多个函数,同时这些函数的参数个数又各不相同,这时可以使用*args,**kwargs 表示不定长度不定类型的参数

from time import ctime, sleep
def timefun(func):#修改wrappedfunc函数参数def wrappedfunc(*args,**kwargs):print("%s called at %s"%(func.__name__, ctime()))#注意这里也要修改func(*args,**kwargs)return wrappedfunc@timefun
def foo1(a,b):print(a+b)@timefun
def foo2(a,b,c):print(a+b+c)foo1(1,2)
foo2(1,2,3)

3.使用装饰器装饰有返回值的函数

如果需要装饰的函数有返回值,那么wrappedfunc函数为了和被装饰函数格式相同,也需要返回值,这个返回值不是乱取的,而是应当将被修饰函数(即下例中wrappedfunc函数中的func函数)的返回值作为wrappedfunc函数的返回值
即使被修饰函数没有返回值,这样的修改也不会错误,因为没有返回值可以认为其返回一个none

from time import ctime, sleep
def timefun(func):def wrappedfunc(*args,**kwargs):print("%s called at %s"%(func.__name__, ctime()))#将被修饰函数的返回值返回return func(*args,**kwargs)return wrappedfunc@timefun
def foo1(a,b):return a+b@timefun
def foo2(a,b,c):print(a+b+c)print(foo1(1,2))
foo2(1,2,3)

通用装饰器

根据以上各例,可以总结出一个能够修饰任意格式的函数的装饰器,即通用装饰器

def fun(func):def innerFunc(*args,**kwargs):...return func(*args,**kwargs)return innerFunc

带参数的装饰器

装饰器在使用时,还可以加参数,传递信息,例如:

@fun("massage")
def hw():print("hello world")

这种带参数的装饰器,和上边举例的装饰器又有所不同,带参数的装饰器由两个闭包构成,其原理和不带参数的装饰器是相同的,结构也不难分析

from time import ctime, sleep
#定义一个变量pre接收传给装饰器的参数,同时将参数默认值设为"hello",如果没有传递参数,则pre为默认值"hello"
def timefun_arg(pre="hello"):def timefun(func):def wrappedfunc(*args,**kwargs):print("%s called at %s"%(func.__name__, ctime()))print(pre)return func(*args,**kwargs)return wrappedfuncreturn timefun@timefun_arg("massage")
def foo1(a,b):return a+b@timefun_arg()
def foo2(a,b,c):print(a+b+c)print(foo1(1,2))
foo2(1,2,3)

Python基础语法-05-装饰器相关推荐

  1. python基础教程:装饰器的高级应用

    装饰器和装饰器模式 装饰器模式是面向对象的一种设计模式,支持将行为动态增加到已经存在的对象上.当装饰一个对象的时候,就表示独立与其他类实例对象,为该对象扩展了新的功能. python的装饰器不是装饰器 ...

  2. python基础教程:装饰器

    1. 函数 在python中,函数通过 def关键字.函数名和可选的参数列表定义.通过 return关键字返回值.我们举例来说明如何定义和调用一个简单的函数: >>> def foo ...

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

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

  4. python基础-函数之装饰器、迭代器与生成器

    1. 函数嵌套 1.1 函数嵌套调用 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数 def bar():print("from in the bar.")def foo ...

  5. python基础(迭代器,生成器,装饰器)

    python: 生成器: 因为当列表元素达到一定上限,列表会占很大内存空间来存储,所以列表是受到内存限制的来适当使用. 生成器可以按照一个算法,循环推导出元素,就不用一次生成整个列表,而通过生成器(g ...

  6. Python深入05 装饰器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法 ...

  7. python基础指令-python基础语法,python 代码命令大全

    python: 1.语法强制缩进 2.区分大小写:iLoop与iloop是两个变量 3.变量无需申明,但是变量赋值前无法使用:a=3合法,b=a+3合法,b=a+c不合法,因为c未赋值前不能使用 4. ...

  8. python基础语法手册-Python学习手册(第4版)pdf

    Python学习手册(第4版) 内容简介 <Python学习手册(第4版)>学习Python的主要内建对象类型:数字.列表和字典.使用Python语句创建和处理对象,并且学习Python的 ...

  9. python基础语法手册-python语法大全,python语法手册

    deff(x,y=0,z=0):pass 定义一个有三个参数x,y,z的函数f,参数x是必须要赋值,比如f(1),f(x=1),都是给x赋值1,y,z也是需要赋值,但不是必须的,因为默认值已经在函数里 ...

  10. python基础代码大全-python基础语法,python 代码命令大全

    python: 1.语法强制缩进 2.区分大小写:iLoop与iloop是两个变量 3.变量无需申明,但是变量赋值前无法使用:a=3合法,b=a+3合法,b=a+c不合法,因为c未赋值前不能使用 4. ...

最新文章

  1. 解析特殊locale的日期格式
  2. Ubuntu下的Linux内核的编译及安装
  3. 有关windows firewall边缘遍历(Edge traversal)的一点信息
  4. ffmpeg系列-协议操作解析-AVIOContext,URLContext,URLProtocol,HTTPContext
  5. SQL Server的还原
  6. [译]简单声明Lua类
  7. java案例教程_JAVA基础案例教程 PDF 下载
  8. 抖音计算机音乐的id,抖音卡点音乐叫什么名字 抖音卡点bgm介绍
  9. Napatech网络加速卡
  10. Koo叔说Shader-Unity中的Shader
  11. Android MTK系统编译与调试命令
  12. 爱快可迅速普及家庭专线?
  13. 设计模式(四)行为型模式介绍及实例(上)
  14. linux定时定点指令
  15. 古剑奇谭网络版服务器正在维护中,古剑奇谭网络版7月19日更新维护公告 古网ol更新了什么...
  16. 基础题库:10 带余除法
  17. 第一章计算机网络概述
  18. Android系统移植与调试之如何修改Android系统默认显示【开发者选项】并默认打开【USB调试】和【未知来源】开关
  19. 代理模式之详谈动态代理模式(Spring的AOP实现)
  20. Docker Compose 搭建 Docker Registry 私服

热门文章

  1. word批量打印助手_如何批量打印数十份甚至上百份Word文档
  2. floyd算法_常用十大算法(九)— 弗洛伊德算法
  3. maya python 游戏与影视编程指南_Maya Python 游戏与影视编程指南
  4. java中是否可以覆盖over_”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?...
  5. ElementUI:table获取复选中的数据
  6. React:react-router
  7. Node.js:简单的node服务器ajax请求
  8. Ubuntu18.04 orb-slam3编译出现的错误 undefined reference to symbol ‘_ZN3MPI8Datatype4FreeEv‘ libmpi_cxx.so
  9. 论文笔记_S2D.02-2013-CVPR-结合三维场景重建和类别分割
  10. 理解线程/多线程处理数组(MultiThreaded dealing with arrays)