一、装饰器是个什么东西?
装饰器可以说是python中的一种编程特性,也可以说是非常好用的一种python编程技巧。
二、它有什么用呢?
在我们编写代码的时候,对于很多函数当我们逻辑写完之后,它就基本不会在修改了(在开发时我们一般情况不会轻易修改源代码)。那么如果有一天我想为它增加一点新功能时,如何保证不修改源代码的情况下增加新的功能呢? 答案就是-->装饰器
三、看一个简单的例子
@是python中的语法糖,@后面跟着的就是我们的装饰器函数
# 现在我们有一个fun函数,在不修改原函数的情况下添加一点额外的功能
def decorator(func):
def wrapper(*args, **kwargs):
print("这是在装饰器中的功能")
func(*args, **kwargs)
return wrapper
@decorator
def fun():
print("this is fun")
# 输出:
这是在装饰器中的功能
this is fun
我们可以从这段代码中简单了解装饰器的基本语法与使用方法。对于其运行流程而言还是比较清晰的,作为有一定基础的人来说应该不成问题-..-。
装饰器要素:内函数使用外函数的变量,外函数返回内函数。
记住这点时非常重要的,这是装饰器的实现基本要素。
在上面我们实现了一个简单的无功能装饰器,现在我们需要来实现一个计时的装饰器
import time
def time_decorator(fun)
def wrapper(*args, **kwargs)
# 在这里真正的执行函数
time1 = time.time()
fun(*args, *kwargs)
time2 = time.time()
print("函数执行时间:", time2 - time1)
return wrapper
@time_decorator()
def fun():
# 让这个函数睡个几秒
time.sleep(3)
上述代码很容易理解吧,当然这是装饰器最简单明了的用法了。
四、带参数的装饰器
对于不带参数的装饰器,其用法,只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数,执行固定逻辑。装饰器传参就是它比较高级的用法,当然我们还是先从简单的例子入手。
例子:根据传入的国家名称打印出不同的问候语言(例子仅仅用于体验器运行流程)
def say_hello(name):
def decortaor(func):
def wrapper(*args, **kwargs):
if name == "china":
print("你好")
else:
print("hello")
# 函数在这里执行
func(*args, **kwargs)
return wrapper
return decortaor
@say_hello("china")
def hello_word():
print("this is hello word")
hello_word()
在这里我们使用了两层嵌套, 第一从层传入的name明显时我们的参数‘china’, 第二层传入的func才是代表了函数本身,
从这个例子我们可以知道,对于装饰器传参的时候,我们需要使用两层的嵌套才能完成传参,对于传参的顺序也必须有固定的顺序。除了上诉例子之外,还有一个应用较多的场景,在网站发请求时可对其请求方式作分类处理(POST, GET等等)。
五、高阶用法-不带参数的类装饰器
我们上面所讲到的装饰器都是基于函数的装饰器,类装饰则是比较高阶的用法。
我们想要实现类装饰器首先在类中必须实现两个魔法方法:__call__以及__init__
他们的作用,__init__:接收被装饰的函数, __call__ :实现装饰逻辑
以代码为例
calss decorator():
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("这是类装饰器")
# 函数真正的执行
self.func(*args, **kwargs)
@decorator
def func():
print("this is func")
func()
函数是通过init传入类中。__call__ 的作用则是重载了“()”,在此情况下,对象就可以使用p()这种格式调用的方法就是call。
六、带参数的类装饰器
其实在实现了带参数的函数装饰器之后,我们对其运行过程就有一定的理解。
下面还是使用一个简单的例子看一看。
class decorator():
def __init__(self, name):
# init接受装饰器传入的参数
self.name = name
# 从call传入函数对象
def __call__(self, func):
def wrapper(*args, **kwargs):
print(self.name)
# 真正的函数执行
func(*args, **kwargs)
return wrapper
@decorator('hello')
def fun():
print("this is fun")
fun()
与不参数的类装饰器不一样的是,函数的传递位置发生了改变。参数通过init传入,函数对象则是通过call传入。
目前就先了解这几种装饰器的原理与实现。
--例子:现在写一个面试常遇见的装饰器实现单例模式(不涉及传参)。
def Singlestance(cls, *args, **kwargs):
_instace = {}
def wrapper():
if cls not in _instace:
_instance[cls] = cls(*args, **kwargs)
return wrapper
@Singlestance
class A()
def __init__(self):
self.name = "lixin"
if __name__ == '__main__':
one = A()
two = A()
print(id(one), id(two))
# 输出
139900931696624 139900931696624
从输出上可以看出来两个对象的地址时一样的,那么也就意味着这两个对象是同一个对象。(单例模式完成)