复杂装饰器
多个装饰器
装饰器只起到装饰作用,所以可以多个装饰器一起使用。
python
"""
在看电影的时候,不仅要过滤年龄,还需要过滤性别
"""
class User:
"""用户模型"""
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def __str__(self):
return self.name
def filter_age_and_gender(func):
def wrapper(*args, **kwargs):
print(args, kwargs)
user = kwargs['user']
if user.age < 18 or user.gender != '女':
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
def filter_age(func):
def wrapper(*args, **kwargs):
print('filter_age')
user = kwargs['user']
if user.age < 18:
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
def filter_gender(func):
def wrapper(*args, **kwargs):
print('filter_gender')
user = kwargs['user']
if user.gender != '女':
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
# 过滤 18 岁以上的女生
# @filter_age_and_gender
@filter_age # 先执行上面的,再执行下面的
@filter_gender
def watch_movie(user=None):
print("%s 正在观看电影" % user)
user1 = User('张三', 17, '女')
user2 = User('李四', 18, '男')
try:
watch_movie(user=user1)
except Exception as e:
print(e)
try:
watch_movie(user=user2)
except Exception as e:
print(e)
"""
面向过程编程 oop: 面向对象 aop:面向切片编程
"""
带参数的装饰器
我们知道下面两种代码是等价的:
python
@dec
def func(...):
...
python
func = dec(func)
我们可以把它当成是纯文本的替换,于是可以是这样的:
python
@dec(arg)
def func(...):
...
python
func = dec(arg)(func)
这也就是我们看到的 带参数
的装饰器。可见,只要 dec(arg)
的返回值满足“装饰器”的定义即可。(接受一个函数,并返回一个新的函数)
案例:
python
"""
在看电影的时候,需要过滤会员等级
"""
class User:
"""用户模型"""
def __init__(self, name, age, gender, level):
self.name = name
self.age = age
self.gender = gender
self.level = level
def __str__(self):
return self.name
def watch_movie(user=None):
print("%s 正在观看电影" % user)
user1 = User('张三', 17, '女', 3)
user2 = User('李四', 18, '男', 4)
try:
watch_movie(user=user1)
except Exception as e:
print(e)
try:
watch_movie(user=user2)
except Exception as e:
print(e)
参考答案
python
class User:
"""用户模型"""
def __init__(self, name, age, gender, level):
self.name = name
self.age = age
self.gender = gender
self.level = level
def __str__(self):
return self.name
def filter_level_3(func):
def wrapper(*args, **kwargs):
user = kwargs['user']
if user.level < 3:
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
def filter_level_4(func):
def wrapper(*args, **kwargs):
user = kwargs['user']
if user.level < 4:
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
def filter_level(level):
def decorator(func):
def wrapper(*args, **kwargs):
user = kwargs['user']
if user.level < level:
raise Exception('%s 不能看电影' % user)
ret = func(*args, **kwargs)
return ret
return wrapper
return decorator
def filter_level_three(): # 对于经常过滤的参数,可以再封装一个函数
return filter_level(3)
# 系统里面的等级是从 1 - 10
# @filter_level_3
# @filter_level(3)
@filter_level_three()
def watch_movie(user=None):
print("%s 正在观看电影" % user)
user1 = User('张三', 17, '女', 3)
user2 = User('李四', 18, '男', 4)
try:
watch_movie(user=user1)
except Exception as e:
print(e)
try:
watch_movie(user=user2)
except Exception as e:
print(e)
类作为装饰器
如果说 Python 里一切都是对象的话,那函数怎么表示成对象呢?其实只需要一个类实现 __call__
方法即可。
python
import time
class Timer:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
before = time.time()
result = self._func(*args, **kwargs)
after = time.time()
print("运行时间:", after - before)
return result
@Timer
def add(x, y=10):
return x + y
也就是说把类的构造函数当成了一个装饰器,它接受一个函数作为参数,并返回了一个对象,而由于对象实现了 __call__
方法,因此返回的对象相当于返回了一个函数。因此该类的构造函数就是一个装饰器。
小结
装饰器中还有一些其它的话题,例如装饰器中元信息的丢失,如何在类及类的方法上使用装饰器等。但本文里我们主要目的是简单介绍装饰器的原因及一般的使用方法,能用上的地方就大胆地用上吧!