Python学习笔记--装饰器
当你要提升自己颜值的时候该如何做? 可以整容,但是能不能在不修改本身器官下来提升颜值?
答案就是装饰器
def add(x,y): print("I am working now") return x+y
你编写了一段程序,但是你觉得功能不够,例如你希望在运行add的时候,增加一段解释性的输出.就可以这样子.
def deco(func): print("The value is ") return func print(deco(add(3,9)))
输出是
I am working now The value is 12这样就做到了在没有改写代码的情况下增加了功能.可能你觉得这个例子有点简单,我们在看一个复杂点的.
需求1:在执行add()方法的时候增加类型检查.
def requires_int(decorated): def inner(*args, **kwargs): kwargs_value=[i for i in kwargs.values()] for arg in list(args)+kwargs_value: print(arg) if not isinstance(arg,int): raise TypeError('%s only accepts integers as arguments'%decorated.__name__) return decorated(*args,**kwargs) return inner def add(x,y): """ Return the sum of x and y""" return x+y add=requires_int(add) add('3',5) TypeError: add only accepts integers as arguments
但是有人疑问,写的太麻烦了,不如直接在代码上加一个类型检查. 但是如果再增加一个需求呢?
需求二:计算程序运行事件.
def addTime(decorated): def inner(x,y): start=time.clock() decorated(x,y) end=time.clock() addTime=(end-start)*1000 print("Time used",addTime) return decorated(x,y) return inner add=addTime(add) Time used 0.03699999999999884
- 如果再有新需求,再写一个装饰器就可以了.是不是很方便?
并且,两个装饰器还可以一起用.像这样
add=addTime(add) add=requires_int(add) add(4,8)
但是这样写感觉好麻烦,每次都要赋值一次,没关系,python为你专门准备了语法糖.其实可以这样写
@addTime @requires_int def add(x,y): """ Return the sum of x and y""" time.sleep(0.7) return x+y
感觉有点眼熟,Django的login_required()不就是这样写的吗.不仅是这样,Flask的路由也是装饰器写的@route.
需要注意的是,如果多个装饰器,运行顺序是从下到上.
瞬间觉得装饰器好有用,不好好学习不行了.
让我们再来学习一个实例. 需求3如果我们要写一个注册表装饰器.#每次运行一个函数,都会有记录这个函数#.
registers=[] def register(decorated): registers.append(decorated) return decorated @register def foo(): return 3 @register def bar(): return 5 for i in registers: print(i())
最后输出3,5. 但是有点问题是,我觉得应该不同的函数的register应该分开.这应该怎么办呢
答案是写一个装饰器类
class Register(object): def __init__(self): self._functions=[]#定义一个列表来装被装饰的函数 def register(self,decorated):#装饰函数 self._functions.append(decorated) return decorated def run_all_function(self, *args, **kwargs): return_values=[] for func in self._functions: return_values.append(func(*args, **kwargs)) return return_values a=Register() b=Register() @a.register def foo(x=5): return x @b.register def bar(x=4): return x @a.register @b.register def baz(x=7): return x print(a.run_all_function()) print(b.run_all_function()) [5, 7] [4, 7]
这样就可以做到互相不影响,每一个register都是不同实例,数值不会混乱,是不是特别方便?
让我们总结一下今天的学习.
装饰器可以在不修改代码下增加功能 装饰器可以叠加 装饰器可以写成一个类