装饰器(最全解释)(转自传智播客就业班)
装饰器是程序开发中经常会⽤到的⼀个功能, ⽤好了装饰器, 开发效率如⻁
添翼, 所以这也是Python⾯试中必问的问题, 但对于好多初次接触这个知识
的⼈来讲, 这个功能有点绕, ⾃学时直接绕过去了, 然后⾯试问到了就挂
了 。
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构,作用是:
1、扩展一个类的功能。
2、动态增加功能,动态撤销。
问题引入:
看一段程序:
#装饰器
#f1() f2() f3() f4()是基本函数
def f1():
pass
def f2():
pass
def f3():
pass
def f4():
pass
#现需要调用这些函数
f1()
f2()
f3()
f4()
现提出新需求:为所有功能添加验证机制, 即: 执⾏函数前, 先进⾏验证。
1.首先可能会想到这样去改:(每次调用函数之前进行验证)
#f1() f2() f3() f4()是基本函数
def f1():
pass
def f2():
pass
def f3():
pass
def f4():
pass
#现需要调用这些函数
#验证函数()
f1()
#验证函数()
f2()
#验证函数()
f3()
#验证函数()
f4()
这种改法简直是太垃圾了。
2.看第二种改法:
#f1() f2() f3() f4()是基本函数
def f1():
#验证函数1()
#验证函数2()
#验证函数3()
pass
def f2():
#验证函数1()
#验证函数2()
#验证函数3()
pass
def f3():
#验证函数1()
#验证函数2()
#验证函数3()
pass
def f4():
#验证函数1()
#验证函数2()
#验证函数3()
pass
#现需要调用这些函数
f1()
f2()
f3()
f4()
这种改法修改的太多了,也垃圾
3.看第三张改法
def check_login():
#验证函数1()
#验证函数2()
#验证函数3()
pass
#f1() f2() f3() f4()是基本函数
def f1():
check_login()
pass
def f2():
check_login()
pass
def f3():
check_login()
pass
def f4():
check_login()
pass
#现需要调用这些函数
f1()
f2()
f3()
f4()
这种改法有进步,但是不符合开放封闭原则,虽然在这个原则是⽤的⾯向对象开发, 但是也适⽤于函数式编程, 简单来说, 它规定已经实现的功能代码不允许被修改,但可以被扩展, 即 :
- 封闭: 已实现的功能代码块
- 开放: 对扩展开发
如果将开放封闭原则应⽤在上述需求中, 那么就不允许在函数 f1 、 f2、 f3、f4的内部进⾏修改代码 。(运用闭包,引用等知识)
func指向f1和f2
这样算是实现了在不该f1()和f2()的提前下增加了验证功能,但是我们又发现一个问题:
原来我么是调用f1()和f2()函数,但现在却执行这两句:
innerFunc = w1(f1) #w1()会返回inner,innerFunc 指向inner
innerFunc()
innerFunc = w1(f2) #w1()会返回inner,innerFunc 指向inner
innerFunc()
这是有问题的。
为了满足上述条件现进行这样修改操作:
#装饰器
def w1(func):
def inner():
print("------验证权限-------")
func()
return inner
def f1():
print("------1-------")
def f2():
print("------2-------")
f1 = w1(f1) #右边的f1指的是f1()函数的引用,而左边的f1是用来接收w1()函数的返回值的!!!!!!!!!!
f1()
f2 = w1(f2)
f2()
离成功又接近了一步!!!!!!
最后改成如下:
#装饰器
def w1(func):
def inner():
print("------验证权限-------")
func()
return inner
@w1 #语法糖 装饰方法
def f1():
print("------1-------")
@w1 #语法糖 装饰方法
def f2():
print("------2-------")
f1()
f2()
语法糖:具有特殊功能,真甜真香,可以让原始的函数和调用方式都不用改变。
语法糖(Syntactic sugar):
- 计算机语言中特殊的某种语法
- 这种语法对语言的功能并没有影响
- 对于程序员有更好的易用性
- 能够增加程序的可读性
简而言之,语法糖就是程序语言中提供**[奇技淫巧]的一种手段和方式而已。 通过这类方式编写出来的代码,即好看又好用**,好似糖一般的语法。固美其名曰:语法糖。
再议装饰 多个装饰器
def makeBold(fn):
print("------xxxxx-------")
def wrapped():
print("------1-------")
return "<b>" + fn() +"</b>"
return wrapped
def makeItalic(fn):
print("------yyyyy-------")
def wrapped():
print("------2-------")
return "<i>" + fn() +"</i>"
return wrapped
@makeBold #后装
@makeItalic #先装
def test3():
print("-----3------")
return "Hello World!!!"
ret = test3()
print(ret)
结果:
------yyyyy-------
------xxxxx-------
------1-------
------2-------
-----3------
<b><i>Hello World!!!</i></b>
这个程序内部怎么运行的非常复杂,可以观看传智播客的就业班视频,讲解的非常详细。
对带有参数的函数进行装饰:
def w1(func):
def inner():
print("------验证权限-------")
func()
return inner
@w1 #语法糖
def f1(a,b):
print("------1-------")
print("------a = %d ,b = %d----------"%(a,b))
@w1 #语法糖
def f2(a,b):
print("------2-------")
print("------a = %d ,b = %d----------" % (a, b))
f1(11,22)
f2(33,44)
程序报错:
Traceback (most recent call last):
File "E:/PythonProgram/test.py", line 36, in <module>
f1(11,22)
TypeError: inner() takes 0 positional arguments but 2 were given
意思是inner()函数不要参数,却给了它两参数
这时应该这样改:
#装饰器
def w1(func):
def inner(aa,bb):
print("------验证权限-------")
func(aa,bb)
return inner
@w1 #语法糖
def f1(a,b):
print("------1-------")
print("------a = %d ,b = %d----------"%(a,b))
@w1 #语法糖
def f2(a,b):
print("------2-------")
print("------a = %d ,b = %d----------" % (a, b))
f1(11,22)
f2(33,44)
对带有返回值的函数进行装饰:
#装饰器
def w1(func):
def inner(aa,bb):
ret = func(aa,bb)
return ret #在这里需要添加return
return inner
@w1 #语法糖
def f1(a,b):
print("------1-------")
print("------a = %d ,b = %d----------"%(a,b))
return("How are you?")
ret = f1(11,22)
print(ret)
结果:
------1-------
------a = 11 ,b = 22----------
How are you?
带有参数的装饰器:在原有装饰器的基础上,设置外部变量,
可以装饰器进行选择
def w1(func):
def inner():
print("-------xxxxxx---------")
func()
return inner
@w1("hahahaha") #带有参数的装饰器
def f1():
print("------1-------")
f1()
报错:
Traceback (most recent call last):
File "E:/PythonProgram/test.py", line 44, in <module>
@w1("hahahaha")
TypeError: inner() takes 0 positional arguments but 1 was given
改法如下: