我们先来看一个嵌套函数
def outliner():
def inliner(name):
print(f"这是一个{name}")
return inliner
f = outliner() # f 实际上是指向inliner这个函数
print(f"这是inliner的地址{f}")
f("闭包") # 这儿实例化f,实际上是实例化inliner, 也就算执行 inliner("闭包")
# 运行结果
这是inliner的地址<function outliner.<locals>.inliner at 0x7f0634432620>
这是一个闭包
现在来引入装饰器,用装饰器实现上述功能
def outliner(func):
def wrapper(name):
func(name)
return wrapper
@outliner
def inliner(name):
print(f"这是一个{name}")
f = inliner #这一步相当于 f = outliner(inliner),返回的是wrapper
print(f"f是指向wrapper的地址:{f}")
f("被装饰的函数") # 实例化f,也就是实例化 wrapper("被装饰的函数")
f是指向wrapper的地址:<function outliner.<locals>.wrapper at 0x7fb781dd38c8>
这是一个被装饰的函数
看到这应该明白装饰器的运行原理了吧?在上述示例中如果要在inliner里要定义多个参数,那么在wrapper里是不是也要定义多个参数,这样的话修改inliner方法的时候也要修改装饰器,这样就太麻烦了,我们可以用魔法参数,稍微改一下就好了,请看下面示例:
def outliner(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
return wrapper
@outliner
def inliner(name):
print(f"这是一个{name}")
f = inliner #这一步相当于 f = outliner(inliner),返回的是wrapper
print(f"f是指向wrapper的地址:{f}")
f("被装饰的函数") # 实例化f,也就是实例化 wrapper("被装饰的函数")
f是指向wrapper的地质:<function outliner.<locals>.wrapper at 0x7fb781dd38c8>
这是一个被装饰的函数
我们在wrapper中定义了两个参数*args和**kwargs,如果不明白这两个参数怎么使用请看这篇文章
当然,装饰器还有更多用法,网上有很多,以后有机会再写,to do…
这儿接下来介绍以下如何把类定义为装饰器,其实了解了上面的原理接下来也很容易了,在介绍类装饰器之前先介绍一下__call__方法:
class Demo:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
print(self.count)
demo = Demo() # 实例化Demo
demo() # 调用 demo , count + 1
demo() # 调用 demo , count + 1
第1次调用
第2次调用
也就是说定义__call__方法后我们可以直接调用实例化的类,假如不定义会报错。
接下来我们定义一个类作装饰器
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self):
return self.func()
@Decorator
def func():
print("this is a func")
f = func # 相当于 f = Decorator(func)
f() # 直接调用实例化的类
# 也可以直接这么写
f = func()
this is a func
this is a func
那如果func()带参数呢?很简单,只要理解了上述所说,其实很简单,看下面
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
@Decorator
def func(name):
print(f"this is a {name}")
f = func("大鸡腿")
this is a 大鸡腿
讲了这么久装饰器的用法,那装饰器到底有什么作用呢?其实装饰器就是用来扩展我们的函数功能。比如你老板叫你设计一个存储电话号码的函数,你辛辛苦苦终于搞定了,老板又叫你拓展一下功能,叫你再把不合格的电话筛选出去,可是你不想再改原来的函数,那装饰器就可以大展身手了,它可以帮你拓展你的方法,大家还可以一起共用。