核心用法:
顾名思义,装饰器就是装饰的器具,如果我们能对函数进行“装饰”,函数会更加灵活。
再通俗的说,我们通过向一个函数传入需要被装饰的函数就能包装一下传入的函数
def our_decorator(func):
def function_wrapper(x):
print("Before calling " + func.__name__)
func(x)
print("After calling " + func.__name__)
return function_wrapper
def foo(x):
print("Hi, foo has been called with " + str(x))
print("We call foo before decoration:")
foo("Hi")
print("We now decorate foo with f:")
foo = our_decorator(foo) #装饰器的核心原理
print("We call foo after decoration:")
foo(42)
""" 输出:
We call foo before decoration:
Hi, foo has been called with Hi
We now decorate foo with f:
We call foo after decoration:
Before calling foo
Hi, foo has been called with 42
After calling foo
"""
我们会思考一个问题:为啥要包装函数呢?直接把要实现的功能写在函数中不就行了吗?
但是如果多个函数都可能需要灵活使用同样的包装,那么每次都改同样代码岂不是很费事
所以针对函数的常见包装,我们就可以考虑使用装饰器
介于装饰器这么有用,每次进行装饰的过程 如 上面的 foo = our_decorator(foo) 都是类似的:传入旧函数到装饰函数
官方直接给了我们一个操作符:'@'
def our_decorator(func):
def function_wrapper(x):
print("Before calling " + func.__name__)
func(x)
print("After calling " + func.__name__)
return function_wrapper
@our_decorator #这一操作就等于 foo = our_decorator(foo)并移动到函数后面
def foo(x):
print("Hi, foo has been called with " + str(x))
初学装饰器可能有点小蒙,因为原理上,出现了多个函数调用的过程,我提供一个简单看懂装饰的办法:
def our_decorator(func):
def function_wrapper(x): # 3.参数x实际先传到了这里 然后开始往下读
print("Before calling " + func.__name__)
func(x) # 4.遇到了原函数,直接返回原函数往下读
print("After calling " + func.__name__) #6.再读剩下的就OK了
return function_wrapper
@our_decorator #1.开始装饰了哦 要明白就是foo = our_decorator(foo)并移动到函数后面
def foo(x): #2.看到函数的参数了吧
print("Hi, foo has been called with " + str(x)) # 5.读完原函数
'''
总结一下:把参数代入到装饰函数中去读,然后遇到原函数再读原函数,这就是经过装饰的函数的效果
什么是装饰函数?:本例中就是 function_wrapper(x)
如何确定装饰函数?:return function_wrapper 装饰器our_decorator 返回的函数就是装饰函数
什么是装饰器:def our_decorator(func) 接受一个函数为参数 并 最终返回一个函数的 函数 就是装饰器
当然这只是核心用法中不严谨的理解
但是 我们最初理解一个东西不用那么严谨,因为那么严谨可能就理解不能了
所以不妨先粗略理解
'''
进阶的一个例子: 装饰器中使用装饰器
"""
背景:我们用装饰器把原函数进行了装饰之后,原函数的某些有用信息会变成新函数的
比如 __name__,__doc__,__module__
但是我们喜欢被装饰后的函数的这些信息还是原来的函数的,因为这部分往往不是我们想要装饰的部分
我们能有一种思路:
进入装饰函数把新函数的类似__name__这些属性换成旧的:
function_wrapper.__name__ = func.__name__
这样做没毛病
但是显然这是一种常见的需求
官方提供了简单的办法给我们
我们通过 from functools import wraps 导入这个办法
即能用@wraps(func) 把装饰用的函数装饰一下,这就是装饰的灵活运用啊
对某项想对函数做的常见操作,我们都能想办法弄一个装饰器去简化代码
这真是一件愉快的事情
"""
from functools import wraps
def greeting(func):
@wraps(func)
def function_wrapper(x):
""" function_wrapper of greeting """
print("Hi, " + func.__name__ + " returns:")
return func(x)
return function_wrapper
高阶的一个例子:把类用作装饰器
"""
背景:python 如此灵活自由,我们不妨想想是否一个类也能作为装饰器呢?
事实上是可以的,如果我们能把一个类像函数一样去用的时候
我们使用一个函数的时候 fun() 加上括号就能用了
类中 这种加上 括号的 使用方法 就是类实例化时候
我们要清晰:装饰器的基本原理 就是 旧函数 = 新函数(旧函数)
我们实例化一个类的时候 t = C(t) 可以在类C的构造器中 接收函数t:def __init__(self, f)
然而 对于一个 实例来说 t()的使用是 类中__call__()方法的使用
所以真正的装饰部分 我们都写到 __call__()中
然后再用 '@' 简化一波操作
不得不说 真的是灵活啊
但是有一个问题:既然我们已经有了函数装饰器了,那么还要类装饰器干嘛捏?
我们思考一下 类与函数的 某些区别:类比函数要有更多功能
因为我们自然能想到:当我们的装饰器逻辑比较复杂,需要抽象多个函数协作的时候,可以写成一个类,然后在类中的__call__()方法中去操作多个函数来装饰函数
这个问题参考了一下https://blog.csdn.net/yitiaodashu/article/details/79016590
不得不说真的是灵活啊!
"""
class decorator2:
def __init__(self, f):
self.f = f
def __call__(self):
print("Decorating", self.f.__name__)
self.f()
@decorator2
def foo():
print("inside foo()")
foo()