聊这个之前先得弄明白2点:
装饰器是干嘛的?
为什么要这个东西?
装饰器的作用,是给一个已经写好的函数增加一些新的功能。在实现这个功能的同时,保证
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式。
我们来试试怎么才能实现这个需求。先定义一个函数,
def test():
print('2018-07-25')
不妨假设我们的需求是在执行test函数体之前,打印出一行‘装饰一哈’。
定义一个函数deco试试,
def deco():
print('装饰一哈')
test()
这样装饰的功能就实现了,但是我们还需要满足其它的需求——不能修改被装饰函数的调用方式——也就是说,我们在完工之后,输入test(),就可以完成现在deco()的功能,
直接把deco赋值给test行不行?
def test():
print('2018-07-25')
def deco():
print('装饰一哈')
test()
test = deco
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
装饰一哈
.
.
.
装饰一哈
装饰一哈
Traceback (most recent call last):
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 10, in <module>
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 6, in deco
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 6, in deco
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 6, in deco
test()
[Previous line repeated 992 more times]
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 5, in deco
print('装饰一哈')
RecursionError: maximum recursion depth exceeded while calling a Python object
Process finished with exit code 1
死循环了,test函数不停地调用自己,好蠢。
我们再试一种方法,把deco()的返回值定义为test函数体,并赋值给之前的变量名test,这样行不行?
def test():
print('2018-07-25')
def deco():
print('装饰一哈')
return test
test = deco()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
Process finished with exit code 0
咦,好像可以哦,再多运行一遍test()试试,
def test():
print('2018-07-25')
def deco():
print('装饰一哈')
return test
test = deco()
test()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
2018-07-25
Process finished with exit code 0
emmmm....不对劲啊,运行一遍test()只多输出了一个‘2018-07-25’,也就是说——‘装饰一哈’是test = deco()语句执行时输出的——搞了半天test还是原来的test....
在IDLE环境里能看得更直观,
>>> def test():
print('2018-07-25')
>>> def deco():
print('装饰一哈')
return test
>>> test = deco()
装饰一哈
>>> test
<function test at 0x03130DB0>
>>> test()
2018-07-25
>>> deco()
装饰一哈
<function test at 0x03130DB0>
>>>
这里就有点奇怪了,既然我执行了语句test = deco(), 那为什么输入test 跟输入deco() 的输出物完全不一样呢?

验一下身份证,
>>> test is deco()
装饰一哈
True
>>> id(test)
51580336
>>> id(deco())
装饰一哈
51580336
>>>
还是一样!
但是我们发现了一件事情,只要语句里面有deco(),输出结果里就一定会多一个‘装饰一哈’....
陷入深深地思考之后,我发现了,原来系统判定test和deco()一样,是判定deco()的返回值和test一样,并且之前执行test = deco()的时候,也仅仅只是将deco()的返回值赋给了test,而deco函数体在从被调用到给出返回值之间的这个计算过程,是不会赋给test的。
这个计算过程是赋不了值的,举个更清晰的例子
>>> 8 is 3 + 5
True
8 虽然is 3 + 5,但是3 + 5 是执行过计算的,8就是8。
完成装饰器之后,我们需要每调用一次test(),‘装饰一哈’就要被输出一次,这时我们就想,能不能把刚才的deco闭个包再看怎么赋值给test?
def test():
print('2018-07-25')
def cover():
def deco():
print('装饰一哈')
test()
return deco
test = cover()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
装饰一哈
.
.
.
装饰一哈
装饰一哈
Traceback (most recent call last):
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 11, in <module>
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 7, in deco
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 7, in deco
test()
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 7, in deco
test()
[Previous line repeated 992 more times]
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 6, in deco
print('装饰一哈')
RecursionError: maximum recursion depth exceeded while calling a Python object
Process finished with exit code 1
又死循环了....好蠢.....
要想个办法不让test调用自己....同时我们想到,这个装饰器不应该是单单为test这个函数设计的,应该所有的函数体都能适用——那么思路就来了,需要在装饰器上增加一个形参,并将函数体内的test改成对应的形参名——再试试,
def test():
print('2018-07-25')
def cover(func):
def deco():
print('装饰一哈')
func()
return deco
test = cover()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
Traceback (most recent call last):
File "C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py", line 10, in <module>
test = cover()
TypeError: cover() missing 1 required positional argument: 'func'
Process finished with exit code 1
test = cover()这句话里面少了一个位置参数,OK,改一下,test = cover(test),
def test():
print('2018-07-25')
def cover(func):
def deco():
print('装饰一哈')
func()
return deco
test = cover(test)
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
Process finished with exit code 0
nice!!
多运行几次test()看看,
def test():
print('2018-07-25')
def cover(func):
def deco():
print('装饰一哈')
func()
return deco
test = cover(test)
test()
test()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
装饰一哈
2018-07-25
装饰一哈
2018-07-25
Process finished with exit code 0
破费。
按照这个思路,我们再试试不对deco()进行闭包操作行不行?
def test():
print('2018-07-25')
def deco(func):
print('装饰一哈')
return func
test = deco(test)
test()
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
2018-07-25
Process finished with exit code 0
不行,不闭包只增加一个形参,跟之前没区别。
那么装饰器的形态大概就出来了,最后在deco()函数体内增加*args和**kw参数,增加泛用性。
def test():
print('2018-07-25')
def cover(func):
def deco(*args, **kw):
print('装饰一哈')
func(*args, **kw)
return deco
test = cover(test)
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
Process finished with exit code 0
最最后,再把test = cover(test)用一个现成的语法糖@cover代替,并把test函数体放在@cover下面。
为什么会有这个语法糖?
为什么要放在下面?
我暂时不想研究了。
def cover(func):
def deco(*args, **kw):
print('装饰一哈')
func(*args, **kw)
return deco
@cover
def test():
print('2018-07-25')
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
Process finished with exit code 0
大功告成。
最最最后,有个小疑问,
deco函数体内的func(*args, **kw)也可以写成return func(*args, **kw)
def cover(func):
def deco(*args, **kw):
print('装饰一哈')
return func(*args, **kw)
return deco
@cover
def test():
print('2018-07-25')
test()
C:\Users\boTTle\PycharmProjects\Ironac\venv\Scripts\python.exe C:/Users/boTTle/PycharmProjects/Ironac/Another_test.py
装饰一哈
2018-07-25
Process finished with exit code 0
不知道这两种在实际运用时有撒区别.....但这篇就先这样吧....后面有问题再说886
——只要原test函数要求带返回值,那么就deco函数体内就必须写成return func(*args, **kw)。
——18/9/27:
装饰器的作用为对现有程序添加新功能,而新功能的执行必须是一段过程代码,原功能则可通过函数体中调用(过程代码)的方式,或者返回值来实现。
而要实现每次调用时新功能都被执行,则这段代码必须要被闭包,并在外层增加一层调用,如此才能实现。因此装饰器必须用高阶函数实现。
本文详细探讨了Python装饰器的设计原理及其应用场景,通过逐步实验解释了如何利用闭包及高阶函数实现装饰器,最终达到为已有函数添加新功能的目的。
792

被折叠的 条评论
为什么被折叠?



