1. 装饰器的应用 python常用的装饰器:
静态方法装饰器 @staticmethod
类方法装饰器 @classmethod
@property
@click
第三方装饰器,快速创建命令行。使用 pip install click
进行安装
1.1 click模块 click使用分为两步骤:
1.使用 @click.command()
装饰一个函数,使之成为命令行接口;
2.使用 @click.option()
等装饰函数,为其添加命令行选项等。
示例 文件名: cmd.py
1 2 3 4 5 6 7 8 9 10 11 12 13 import click@click.command() @click.option('--count', default=1, help='Number of greetings.') @click.option('--name', prompt='Your name', help='The person to greet.') def hello (count, name) : for x in range(count): click.echo('Hello %s!' % name) if __name__ == '__main__' : hello()
获取命令行的说明 1 2 # helppython cmd.py --help
输出:
1 2 3 4 5 6 Usage: cmd.py [OPTIONS] Options: --count INTEGER Number of greetings. --name TEXT The person to greet. --help Show this message and exit.
运行 1 python cmd.py --count 2 --name tyltr
输出:
1 2 Hello tyltr! Hello tyltr!
2.函数装饰器 装饰器的原理:python中一切皆对象,函数也是对象。把函数(不妨称为函数A)作为作为另一个函数(不妨称为函数B)的参数,即可实现扩展B的功能
2.1 统计执行时间 无参数的装饰器示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import timedef statime (func) : """ 统计运行时间 :param func: 装饰的函数 :return: """ def inner (*args, **kwargs) : start = time.time() res = func(*args, **kwargs) print(time.time() - start) return res return inner @statime def add (x,y) : time.sleep(1 ) return x+y if __name__ == '__main__' : a = add(1 ,2 ) print(a)
输出:
2.2 校验参数 有参数的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import functoolsdef check_args (type_) : def wrapper (func) : @functools.wraps(func) # “修复”函数名 def inner (args) : if not isinstance(args, type_): raise Exception("类型有误" ) return func(args) return inner return wrapper @check_args(int) def div (args) : return args / 2 if __name__ == '__main__' : t = div(4 ) print(t)
上例demo 可以实现参数的校验。
3. 类装饰器 大部分装饰器都基于函数和闭包实现的。
3.1 类调用 实现了魔法方法 __call__
的对象,可以像函数一样被调用。
示例 1 2 3 4 5 6 7 8 9 class Foo (object) : def __call__ (self, *args, **kwargs) : print('qqq' ) if __name__ == '__main__' : f = Foo() if callable(f): f()
运行结果:
说明: callable(obj) 判断是否是可调用的类型。如果可调用,则返回 True
;否则 False
可调用对象:函数、方法、实现了__call__
的对象。 示例:
1 2 3 4 5 6 7 def p () : print("pppp" ) if __name__ == '__main__' : print(callable(p))
输出 : True
3.2 实现类装饰器 实现一个装饰器:在每次函数执行前都会等到 duration
秒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import timeimport functoolsclass 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) : return self.func(*args, **kwargs) def delay (duration) : """装饰器:推迟某个函数的执行。同时提供 .eager_call 方法立即执行 """ return functools.partial(DelayFunc, duration) @delay(duration=2) def add (a, b) : return a + b add(1 , 2 ) add.eager_call(1 , 2 )
3.3 使用 wrapt 模块编写更扁平的装饰器 在写装饰器时候,经常会遇到下面两类问题:
示例 一个随机生成[min,max]之间的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import randomdef randomint (min, max) : def inner (func) : def wapper (*args, **kwargs) : num = random.randint(min, max) return func(num, *args, **kwargs) return wapper return inner @randomint(1, 45) def p (num) : print(num) p()
这个示例多次嵌套,绕来绕去。。。。
而且不能用于类方法上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import randomdef randomint (min, max) : def inner (func) : def wapper (*args, **kwargs) : num = random.randint(min, max) return func(num, *args, **kwargs) return wapper return inner class Example () : @randomint(1, 45) def p (self, num) : print(num) e = Example() e.p()
所以,需要有一个更好的方法解决这些问题。那就需要 wrapt
。
上例可以改为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import randomimport wraptdef randomint (min, max) : @wrapt.decorator def inner (wrapped, instance, args, kwargs) : num = random.randint(min, max) return wrapped(num, *args, **kwargs) return inner class Example () : @randomint(1, 45) def p (self, num) : print(num) e = Example() e.p()
wrapt使用说明 1.无参数的装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import wraptimport time@wrapt.decorator def statime (wrapped, instance, args, kwargs) : s = time.time() res = wrapped(*args, **kwargs) print(time.time() - s) return res @statime def add (x, y) : time.sleep(1 ) return x + y if __name__ == '__main__' : res = add(1 , 2 ) print(res)
说明:
使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs)
第一个参数wrapped 表示被装饰的函数或类方法,说明:
1 2 3 如果被装饰者为普通类方法,该值为类实例 如果被装饰者为 classmethod 类方法,该值为类 如果被装饰者为类/函数/静态方法,该值为 None
第二个参数instance是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据instance的值你能够更加灵活的调整你的装饰器。
args和kwargs也是固定的,注意前面没有星号
偏函数partial 在应用中,部分参数已经知晓,其他参数未知的情况下,传入已经知晓的参数创建一个实例。补齐其他参数后,即可运行这个新实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import functoolsclass Dog () : def __init__ (self, age, name, *args, **kwargs) : self.age = age self.name = name jingba = functools.partial(Dog, name="jingba" ) print(jingba) d1 = jingba(age=1 ) print(d1) print(d1.name) print(d1.age)