装饰器是一种函数的函数,因为装饰器传入的参数就是一个函数,然后通过实现各种功能来对这个函数的功能进行增强。
装饰器的本质:函数的嵌套调用。
- 以下由浅入深演示各种装饰器的调用过程:
无参函数 的装饰器
# -*- encoding: utf-8 -*-
import time
def wrapper(target_func):
def inner():
start = time.time()
target_func()
end = time.time()
run_time = end - start
return "RUN TIME FOR FUNCTION 【%s】 IS %s S." % (target_func.__name__, run_time)
return inner
# 语法糖调用方式1,等价于普通调用方式2
@wrapper
def demo1():
time.sleep(2)
print(demo1())
# 普通调用方式2
def demo2():
time.sleep(2)
w = wrapper(demo2)
print(w())
含参函数 的装饰器
装饰器内部 需要获取到 被装饰(原)函数 的参数。
def wrapper1(target_func):
def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
target_func()
num1 = args[0]
num2 = kwargs["num2"]
result = num1 + num2
return "THE RESULT FOR FUNCTION 【%s】 IS %s." % (target_func.__name__, result)
return inner
@wrapper1
def demo1(*args, **kwargs):
print("JUST DO IT")
print(demo1(10, num2=20))
有返回值的含参函数 的装饰器
装饰器 需要返回 被装饰(原)函数 的返回值。
def wrapper2(target_func):
def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
return_user = target_func(*args, **kwargs)
# 目标函数的返回值用变量`return_user` 接收
if return_user['is_super'] == True: # 判断用户是否为超级用户
return "THE USER IS SUPER USER !"
return "NORMAL USER."
return inner
@wrapper2
def demo2(id, username, is_super):
user = {
"id": id,
"username": username,
"is_super": is_super
}
return user
print(demo2(1214, 'xaunRui', True))
print(demo2(52221, 'Jack', False))
无参函数 的含参装饰器
装饰器本身 需要参数。
def wrapper3(*args, **kwargs): # 此处也可以直接定义参数`log_level`
log_level = args[0]
def wrapper_inner(target_func):
def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
return_value = target_func(*args, **kwargs)
# 目标函数的返回值用变量 `return_value` 接收
if return_value == "some error" and log_level == "ERROR":
return "ERROR!
return "IT'S OK OR DON'T CARE."
return inner
return wrapper_inner
# @wrapper3("ERROR")
@wrapper3("WARRING")
def demo3():
return "some error"
print(demo3())
类装饰器
基于类装饰器的实现,必须实现
__call__
和__init__
两个内置函数。
__init__
:接收被装饰函数,__call__
:实现装饰逻辑。
不含参的类装饰器
class logger(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("[INFO]:log for {func}().".format(func=self.func.__name__))
return self.func(*args, **kwargs)
@logger
def say(something):
print("say {}!".format(something))
say("hello")
含参的类装饰器
class logger(object):
def __init__(self, level='INFO'): # 默认为`INFO`
self.level = level
def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print("[{level}]: log for {func}()."\
.format(level=self.level, func=func.__name__))
func(*args, **kwargs)
return wrapper #返回函数
@logger(level='WARNING')
def say(something):
print("say {}!".format(something))
say("hello")
functools.wraps
装饰器
-
示例:
def wrapper(func): def inner_function(): pass return inner_function # 方式1: @wrapper def wrapped(): pass print(wrapped.__name__) # 方式2: def wrapped(): pass print(wrapper(wrapped).__name__) # inner_function # 返回的并不是func,原因如下: # 方式1执行func和方式2decorator(func)是等价的,所以上面 func.__name__ 等价于#decorator(func).__name__ 的,所以函数名是 inner_function
-
为了避免这种情况,使用
functools .wraps
装饰器,它的作用就是将被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合直觉,如下:from functools import wraps def wrapper(func): @wraps(func) # 此处! def inner_function(): pass return inner_function @wrapper def wrapped(): pass print(wrapped.__name__) # wrapped
-
实际使用偏函数,源码如下:
def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
property
装饰器
-
示例,学生类实例,通常的属性操作方法:
class Student(object): def __init__(self, name, age=None): self.name = name self.age = age # 实例化 xiaoming = Student("小明") # 添加属性 xiaoming.age=25 # 查询属性 xiaoming.age # 删除属性 del xiaoming.age
-
如上操作对象属性,会直接暴露属性,并且不能对属性的值做合法性限制,手动实现
setter
:class Student(object): def __init__(self, name): self.name = name self.name = None def set_age(self, age): if not isinstance(age, int): raise ValueError('输入不合法:年龄必须为数值!') if not 0 < age < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=age def get_age(self): return self._age def del_age(self): self._age = None xiaoming = Student("小明") # 添加属性 xiaoming.set_age(25) # 查询属性 xiaoming.get_age() # 删除属性 xiaoming.del_age()
-
如上的方法,满足了要求,但不太符合
对象.属性名
的风格,使用property
:class Student(object): def __init__(self, name): self.name = name self.name = None @property def age(self): return self._age @age.setter def age(self, value): if not isinstance(value, int): raise ValueError('输入不合法:年龄必须为数值!') if not 0 < value < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=value @age.deleter def age(self): del self._age xiaoming = Student("小明") # 设置属性 xiaoming.age = 25 # 查询属性 xiaoming.age # 删除属性 del xiaoming.age
@property
装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。**同时,会将这个函数变成另外一个装饰器。**如:@age.setter
和@age.deleter
。@age.setter
使XiaoMing.age = 25
的方式可以直接赋值。@age.deleter
使del XiaoMing.age
的方式可以删除属性。