装饰器
1.什么是装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
2.需要装饰器的需求(原因)
想象一下可以假如公司有部门有两个函数。
def in_office(): # 进入办公室
print("已经进入办公室")
def in_hall(): # 进入大厅
print("已经进入大厅") # bug here
in_office()
in_hall()
输出结果:
已经进入办公室 已经进入大厅
随着公司的发展,办公室不是谁想进就可以,需要某些权限的人才能进,这时候就需要一个验证功能。
一个员工是这样改的:
def in_office(): # 进入办公室
print('进行验证')
print("已经进入办公室")
def in_hall(): # 进入大厅
print('进行验证')
print("已经进入大厅") # bug here
in_office()
in_hall()
结果他一周就被开除了。大家发现了,这样很繁琐是不是?公司老板发出邀请,谁要是能解决这个问题,工资涨1000.不一会就有一个人站了出来,说“我能做,用个装饰器就行了”
def check(func):
def inner(name):
if name < 5: #这里表示小于5的权限可以进入,大的没有权限
print('可以进入')
return func
else:
print('权限不足')
return inner
@check
def in_office(): # 进入办公室
print('进行验证')
print("已经进入办公室")
def in_hall(): # 进入大厅
print('进行验证')
print("已经进入大厅") # bug here
in_office(8)
结果输出:
没有权限
解释:
如果是第一次见装饰器的话,那这段代码可能会看着有点迷糊。我用我们之前学到的闭包来解释一下,重新把这个代码写一遍。
看到这个用闭包知识解释的装饰器,大家是不是就比较明白了。我的理解是装饰器就是一个闭包加一个函数。
3.装饰器时间问题
def check(func):
print('已经装饰了')
def inner(name):
if name < 5:
print('可以进入')
return func
else:
print('权限不足')
return inner
@check #只要python解释器执行到这,就会进行自动的装饰 只是在调用的时候才把装饰器产生的
def in_office(): # 进入办公室 # 效果发挥出来
print('进行验证')
print("已经进入办公室")
结果输出:
已经装饰了
4.装饰器的顺序问题
def makeBold(fn):
def wrapped():
return "<b>"+fn()+"</b>" # 给函数输出左右加b
return wrapped
def makeItalic(fn):
def wrapped():
return "<i>"+fn()+"</i>" # 给函数输出左右加i
return wrapped
@makeBold
@makeItalic
def test3():
return "hello world-3"
print(test3())
解释:
按照正常理解,先执行了加b装饰器,又执行了加i装饰器,结果应该是<i><b>hello world-3</b></i>
但是结果是:<b><i>hello world-3</i></b>。对!装饰器的顺序是谁挨近,谁先执行。
5.装饰器参数问题
在参数这边我可能讲得不是太清楚,所以在这里分类一下装饰器的参数问题。
参数的分类大致有这么几种:
函数有参 函数有参
装饰器无参 装饰器有参
函数无参 函数无参
刚才那个我展示的是函数有参、装饰器无参,是这几个中等的。我这里分类是为了区分哪个是函数参、哪个是装饰器参。下面我就写一个特例:函数有不定长参,但装饰器无参的。
def debug(func):
def wrapper(*args, **kwargs): # 指定不定长参数
print("[DEBUG]: enter {}()".format(func.__name__))
return func(*args, **kwargs) # 这里和上面相搭配
return wrapper # 返回
@debug # 功能是在执行函数前,输出该函数的函数名
def say(something):
print("hello {}!".format(something))
say('小米')
解释:
say()函数的参数可以是一个,也可以是两个或者一个列表都行。
6.函数的应用
前几天我在看电视《我爱发明》的时候,看到有人发明了一个自动筛选果子大小的机器,于是我灵机一动,可以拿这个作为装饰器的例子啊!
# 装饰器例子
def split(func):
list2, list3 = [], [] # list2放小果子 list3放大果子
def inner(apples):
for apple in apples:
if apple >5:
list3.append(apple)
else:
list2.append(apple)
# print(list2,list3)
return func
return inner
@split
def collect(args):
for s in args:
print(s)
list1 = [1,5,8,7,3,4,9,1,8,2,6,7,2,5,3,8,4]
collect(list1)
结果输出:
[1, 5, 3, 4, 1, 2, 2, 5, 3, 4] [8, 7, 9, 8, 6, 7, 8]
解释:
这个装饰器,实现了一个果子大小分类的这么一个作用。在我理解,装饰器就是添加功能的。虽然功能很简单,但是应该解释了那个道理了。