1. 将这部分相同的功能封装成函数,然后在使用这个功能的时候程序中调用这个函数
2. 将这部分相同的功能封装成装饰器,
然后在使用这个功能的时候在需要处理的方法前加上对应的装饰器
这两种方式各有好处,我认为
1中的方式更适合新加功能,通过单独新加函数实现功能,然后在需要的位置使用,
2中的方式更适合固定且经常使用的功能,比如说日志记录,执行时间计算等,只需要在需要用到该功能的时候直接在该方法前加装饰器即可
下面我们先看个例子:
import time
def origin_fun(a, b):
start_time = time.time()
s = a + b
exec_time = time.time() - start_time
print('执行时间: {}'.format(exec_time))
return s
以上是原函数,我们要得到传入两个参数的加和 *** 作的时间消耗,我们如果需要对很多 *** 作进行时间记录,有两个方法
1、拆出函数实现def func(a, b):
return a+b
def get_begin_date():
return time.time()
def final_func(a, b):
start_time = get_begin_date()
s = func(a, b)
exec_time = get_begin_date() - start_time
print('执行时间: {}'.format(exec_time))
return s
可以发现我们这样是可以实现的, 但是这样再使用的时候还是有点繁琐,接下来我们用装饰器实现
2、装饰器实现 (1)我们先来介绍一下装饰器的一个结构:简单理解就是将添加了装饰器的函数(func)传入装饰器函数(decorator)中,在装饰器函数中创建内置函数(wrapper),内置函数实现通用的功能并真正执行传入的函数(func)之后将返执行的函数结果在内置函数(wrapper)中返回,在装饰器函数(decorator)中将装饰器的内置函数(wrapper)作为返回对象返回
def decorator(func):
def wrapper(*args, **kwargs):
"""
define args and kwargs
"""
# 通用的功能,这里我们认为输出传入的函数名称就是通用功能
print(func.__name__)
# 执行传入的方法
return func(*args, **kwargs)
# 返回当前方法的内置函数
return wrapper
@decorator
def func(a, b):
return a + b
这么一看的话大家应该就会觉得装饰器也很好理解了。
(2)下面我们就将上述的例子通过装饰器实现 a. 无参装饰器
def time_local(func):
def wrapper(*args, **kwargs):
start_time = time.time()
f = func(*args, **kwargs)
exec_time = time.time() - start_time
print('执行时间: {}'.format(exec_time))
return f
return wrapper
@time_local
def func1(a, b):
return a+b
@time_local
def func2(a, b):
return a*b
上述代码块就是我们的装饰器实现,可以发现我们在计算其他 *** 作的消耗的时间的时候只需要在函数前添加装饰器就可以了,这样就代码的改动就会尽可能少的改动,这就是简单的装饰器的实现方式和应用方式。
b. 有参装饰器高级一点的我们可能需要对装饰器进行传参,这样的装饰器就是在无参装饰器外再包一层函数,如下
def time_local_args(level):
def time_local(func):
def wrapper(*args, **kwargs):
if level:
start_time = time.time()
f = func(*args, **kwargs)
exec_time = time.time() - start_time
print('执行时间: {}'.format(exec_time))
else:
f = func(*args, **kwargs)
return f
return wrapper
return time_local
@time_local_args(level=True)
def func(a, b):
return a+b
这样一看的话就很清晰了吧,装饰器就是一个函数内嵌套一个内置函数,在内置函数中实现通用功能以及执行被装饰的函数,嵌套函数的返回值为被装饰的函数,装饰器函数返回内置函数对象。
c. 再优雅一些的写法, 传参并封装成类且有参数的装饰器class TimeLocal:
def __init__(self, level):
self._level = level
def __call__(self, func, *args, **kwargs):
def wrapper(*args, **kwargs):
if self._level:
start_time = time.time()
f = func(*args, **kwargs)
exec_time = time.time() - start_time
print('执行时间: {}'.format(exec_time))
else:
f = func(*args, **kwargs)
return f
return wrapper
@TimeLocal(level=True)
def func4(a, b):
return b / a
d.偏函数与类结合构建装饰器
事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。
对于这个 callable 对象,我们最熟悉的就是函数了。
除函数之外,类也可以是 callable 对象,只要实现了__call__ 函数(上面几个例子已经接触过了)。
还有容易被人忽略的偏函数其实也是 callable 对象。
接下来就来说说,如何使用 类和偏函数结合实现一个与众不同的装饰器。
如下所示,DelayFunc 是一个实现了 call 的类,delay 返回一个偏函数,在这里 delay 就可以做为一个装饰器。(以下代码摘自 Python工匠:使用装饰器的小技巧)
import time
import functools
class DelayFunc:
def __init__(self, duration, func):
self.duration = duration
self.func = func
def __call__(self, *args, **kwargs):
print(f'Wait for {self.duration} seconds...')
time.sleep(self.duration)
return self.func(*args, **kwargs)
def eager_call(self, *args, **kwargs):
print('Call without delay')
return self.func(*args, **kwargs)
def delay(duration):
"""
装饰器:推迟某个函数的执行。
同时提供 .eager_call 方法立即执行
"""
# 此处为了避免定义额外函数,
# 直接使用 functools.partial 帮助构造 DelayFunc 实例
return functools.partial(DelayFunc, duration)
@delay(duration=2)
def add(a, b):
return a+b
以上就是我们装饰器的理解以及几张实现的方式,最后我们看一下用在类上的装饰器的代码
instances = {}
def singleton(cls):
def get_instance(*args, **kw):
cls_name = cls.__name__
print('===== 1 ====')
if not cls_name in instances:
print('===== 2 ====')
instance = cls(*args, **kw)
instances[cls_name] = instance
return instances[cls_name]
return get_instance
@singleton
class User:
_instance = None
def __init__(self, name):
print('===== 3 ====')
self.name = name
print(User('sdma'))
>>> ===== 1 ====
>>> ===== 2 ====
>>> ===== 3 ====
>>> <__main__.User object at 0x000001EAC67AEC48>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)