1. 首页
  2. Python

终于有人把python装饰器讲的明明白白的,太棒了

“u003Cdivu003Eu003Cpu003E对于每一个学习 Python 的同学,想必对 @ 符号一定不陌生了,正如你所知, @ 符号是装饰器的语法糖,@符号后面的函数就是我们本文的主角:装饰器。u003Cu002Fpu003Eu003Cpu003E装饰器放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为 装饰器 。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Ffc2d7e4c8a9847e8b493c0490c6ef059″ img_width=”400″ img_height=”301″ alt=”终于有人把python装饰器讲的明明白白的,太棒了” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003E01. 装饰器语法糖u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖。u003Cu002Fpu003Eu003Cpu003E它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰函数 或 装饰器。u003Cu002Fpu003Eu003Cpu003E你要问我装饰器可以实现什么功能?我只能说你的脑洞有多大,装饰器就有多强大。u003Cu002Fpu003Eu003Cpu003E装饰器的使用方法很固定:u003Cu002Fpu003Eu003Culu003Eu003Cliu003E先定义一个装饰函数(帽子)(也可以用类、偏函数实现)再定义你的业务函数、或者类(人)最后把这顶帽子带在这个人头上u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E装饰器的简单的用法有很多,这里举两个常见的。u003Cu002Fpu003Eu003Colu003Eu003Cliu003E日志打印器时间计时器u003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003Eu003Cstrongu003E02. 入门用法:日志打印器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E首先是日志打印器。 实现的功能:u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E在函数执行前,先打印一行日志告知一下主人,我要执行函数了。 在函数执行完,也不能拍拍屁股就走人了,咱可是有礼貌的代码,再打印一行日志告知下主人,我执行完啦。u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpreu003E# 这是装饰函数u003Cbru003Edef logger(func):u003Cbru003E def wrapper(*args, **kw):u003Cbru003E print(‘我准备开始计算:{} 函数了:’.format(func.__name__))u003Cbru003E # 真正执行的是这行。u003Cbru003E func(*args, **kw)u003Cbru003E print(‘啊哈,我计算完啦。给自己加个鸡腿!!’)u003Cbru003E return wrapperu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E假如,我的业务函数是,计算两个数之和。写好后,直接给它带上帽子。u003Cu002Fpu003Eu003Cpreu003E@loggeru003Cbru003Edef add(x, y):u003Cbru003E print(‘{} + {} = {}’.format(x, y, x+y))u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E然后我们来计算一下。u003Cu002Fpu003Eu003Cpreu003Eadd(200, 50)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E快来看看输出了什么,神奇不?u003Cu002Fpu003Eu003Cpreu003E我准备开始计算:add 函数了:u003Cbru003E200 + 50 = 250u003Cbru003E啊哈,我计算完啦。给自己加个鸡腿!u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E03. 入门用法:时间计时器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E再来看看 时间计时器 实现功能:u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E顾名思义,就是计算一个函数的执行时长。u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpreu003E# 这是装饰函数u003Cbru003Edef timer(func):u003Cbru003E def wrapper(*args, **kw):u003Cbru003E t1=time.time()u003Cbru003E # 这是函数真正执行的地方u003Cbru003E func(*args, **kw)u003Cbru003E t2=time.time()u003Cbru003E # 计算下时长u003Cbru003E cost_time = t2-t1 u003Cbru003E print(“花费时间:{}秒”.format(cost_time))u003Cbru003E return wrapperu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E假如,我们的函数是要睡眠10秒(冏~,小明实在不知道要举什么例子了)。这样也能更好的看出这个计算时长到底靠不靠谱。u003Cu002Fpu003Eu003Cpreu003Eimport timeu003Cbru003E@timeru003Cbru003Edef want_sleep(sleep_time):u003Cbru003E time.sleep(sleep_time)u003Cbru003Ewant_sleep(10)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E来看看,输出。真的是10秒耶。真历害!!!u003Cu002Fpu003Eu003Cpreu003E花费时间:10.0073800086975098秒u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E04. 进阶用法:带参数的函数装饰器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E通过上面简单的入门,你大概已经感受到了装饰的神奇魅力了。u003Cu002Fpu003Eu003Cpu003E不过,装饰器的用法远不止如此。我们今天就要把这个知识点讲透。u003Cu002Fpu003Eu003Cpu003E上面的例子,装饰器是不能接收参数的。其用法,只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数,执行固定逻辑。u003Cu002Fpu003Eu003Cpu003E如果你有经验,你一定经常在项目中,看到有的装饰器是带有参数的。u003Cu002Fpu003Eu003Cpu003E装饰器本身是一个函数,既然做为一个函数都不能携带函数,那这个函数的功能就很受限。只能执行固定的逻辑。这无疑是非常不合理的。而如果我们要用到两个内容大体一致,只是某些地方不同的逻辑。不传参的话,我们就要写两个装饰器。小明觉得这不能忍。u003Cu002Fpu003Eu003Cpu003E那么装饰器如何实现传参呢,会比较复杂,需要两层嵌套。u003Cu002Fpu003Eu003Cpu003E同样,我们也来举个例子。u003Cu002Fpu003Eu003Cpu003E我们要在这两个函数的执行的时候,分别根据其国籍,来说出一段打招呼的话。u003Cu002Fpu003Eu003Cpreu003Edef chinese():u003Cbru003E print(“我来自中国。”)u003Cbru003Edef american():u003Cbru003E print(“I am from America.”)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E在给他们俩戴上装饰器的时候,就要跟装饰器说,这个人是哪国人,然后装饰器就会做出判断,打出对应的招呼。u003Cu002Fpu003Eu003Cpu003E戴上帽子后,是这样的。u003Cu002Fpu003Eu003Cpreu003E@say_hello(“china”)u003Cbru003Edef chinese():u003Cbru003E print(“我来自中国。”)u003Cbru003E@say_hello(“america”)u003Cbru003Edef american():u003Cbru003E print(“I am from America.”)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E万事俱备,只差帽子了。来定义一下,这里需要两层嵌套。u003Cu002Fpu003Eu003Cpreu003Edef say_hello(country):u003Cbru003E def wrapper(func):u003Cbru003E def deco(*args, **kwargs):u003Cbru003E if country == “china”:u003Cbru003E print(“你好!”)u003Cbru003E elif country == “america”:u003Cbru003E print(‘hello.’)u003Cbru003E else:u003Cbru003E returnu003Cbru003E # 真正执行函数的地方u003Cbru003E func(*args, **kwargs)u003Cbru003E return decou003Cbru003E return wrapperu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E执行一下u003Cu002Fpu003Eu003Cpreu003Eamerican()u003Cbru003Eprint(“————“)u003Cbru003Echinese()u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E看看输出结果。u003Cu002Fpu003Eu003Cpreu003E你好!u003Cbru003E我来自中国。u003Cbru003E————u003Cbru003Ehello.u003Cbru003EI am from Americau003Cbru003Eu003Cu002Fpreu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Ff1b77d707c114e02954c8399780c279a” img_width=”537″ img_height=”422″ alt=”终于有人把python装饰器讲的明明白白的,太棒了” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003E05. 高阶用法:不带参数的类装饰器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E以上都是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。u003Cu002Fpu003Eu003Cpu003E基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。 __init__ :接收被装饰函数 __call__ :实现装饰逻辑。u003Cu002Fpu003Eu003Cpreu003Eclass logger(object):u003Cbru003E def __init__(self, func):u003Cbru003E self.func = funcu003Cbru003E def __call__(self, *args, **kwargs):u003Cbru003E print(“[INFO]: the function {func}() is running…”\u003Cbru003E .format(func=self.func.__name__))u003Cbru003E return self.func(*args, **kwargs)u003Cbru003E@loggeru003Cbru003Edef say(something):u003Cbru003E print(“say {}!”.format(something))u003Cbru003Esay(“hello”)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E执行一下,看看输出u003Cu002Fpu003Eu003Cpreu003E[INFO]: the function say() is running…u003Cbru003Esay hello!u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E06. 高阶用法:带参数的类装饰器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E上面不带参数的例子,你发现没有,只能打印INFO级别的日志,正常情况下,我们还需要打印DEBUG WARNING等级别的日志。 这就需要给类装饰器传入参数,给这个函数指定级别了。u003Cu002Fpu003Eu003Cpu003E带参数和不带参数的类装饰器有很大的不同。u003Cu002Fpu003Eu003Cpu003E__init__ :不再接收被装饰函数,而是接收传入参数。 __call__ :接收被装饰函数,实现装饰逻辑。u003Cu002Fpu003Eu003Cpreu003Eclass logger(object):u003Cbru003E def __init__(self, level=’INFO’):u003Cbru003E self.level = levelu003Cbru003E def __call__(self, func): # 接受函数u003Cbru003E def wrapper(*args, **kwargs):u003Cbru003E print(“[{level}]: the function {func}() is running…”\u003Cbru003E .format(level=self.level, func=func.__name__))u003Cbru003E func(*args, **kwargs)u003Cbru003E return wrapper #返回函数u003Cbru003E@logger(level=’WARNING’)u003Cbru003Edef say(something):u003Cbru003E print(“say {}!”.format(something))u003Cbru003Esay(“hello”)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E我们指定WARNING级别,运行一下,来看看输出。u003Cu002Fpu003Eu003Cpreu003E[WARNING]: the function say() is running…u003Cbru003Esay hello!u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E07. wraps 装饰器有啥用?u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E在 functools 标准库中有提供一个 wraps 装饰器,你应该也经常见过,那他有啥用呢?u003Cu002Fpu003Eu003Cpu003E先来看一个例子u003Cu002Fpu003Eu003Cpreu003Edef wrapper(func):u003Cbru003E def inner_function():u003Cbru003E passu003Cbru003E return inner_functionu003Cbru003E@wrapperu003Cbru003Edef wrapped():u003Cbru003E passu003Cbru003Eprint(wrapped.__name__)u003Cbru003E#inner_functionu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E为什么会这样子?不是应该返回 func 吗?u003Cu002Fpu003Eu003Cpu003E这也不难理解,因为上边执行func 和下边 decorator(func) 是等价的,所以上面 func.__name__ 是等价于下面decorator(func).__name__ 的,那当然名字是 inner_functionu003Cu002Fpu003Eu003Cpreu003Edef wrapper(func):u003Cbru003E def inner_function():u003Cbru003E passu003Cbru003E return inner_functionu003Cbru003Edef wrapped():u003Cbru003E passu003Cbru003Eprint(wrapper(wrapped).__name__)u003Cbru003E#inner_functionu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E那如何避免这种情况的产生?方法是使用 functools .wraps 装饰器,它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉。u003Cu002Fpu003Eu003Cpreu003Efrom functools import wrapsu003Cbru003Edef wrapper(func):u003Cbru003E @wraps(func) u003Cbru003E def inner_function():u003Cbru003E passu003Cbru003E return inner_functionu003Cbru003E@wrapperu003Cbru003Edef wrapped():u003Cbru003E passu003Cbru003Eprint(wrapped.__name__)u003Cbru003E# wrappedu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E准确点说,wraps 其实是一个偏函数对象(partial),源码如下u003Cu002Fpu003Eu003Cpreu003Edef wraps(wrapped,u003Cbru003E assigned = WRAPPER_ASSIGNMENTS,u003Cbru003E updated = WRAPPER_UPDATES):u003Cbru003E return partial(update_wrapper, wrapped=wrapped,u003Cbru003E assigned=assigned, updated=updated)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E可以看到wraps其实就是调用了一个函数update_wrapper,知道原理后,我们改写上面的代码,在不使用 wraps的情况下,也可以让 wrapped.__name__ 打印出 wrapped,代码如下:u003Cu002Fpu003Eu003Cpreu003Efrom functools import update_wrapperu003Cbru003EWRAPPER_ASSIGNMENTS = (‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’,u003Cbru003E ‘__annotations__’)u003Cbru003Edef wrapper(func):u003Cbru003E def inner_function():u003Cbru003E passu003Cbru003E update_wrapper(inner_function, func, assigned=WRAPPER_ASSIGNMENTS)u003Cbru003E return inner_functionu003Cbru003E@wrapperu003Cbru003Edef wrapped():u003Cbru003E passu003Cbru003Eprint(wrapped.__name__)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E08. 使用偏函数与类实现装饰器u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。u003Cu002Fpu003Eu003Cpu003E事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。u003Cu002Fpu003Eu003Cpu003E对于这个 callable 对象,我们最熟悉的就是函数了。u003Cu002Fpu003Eu003Cpu003E除函数之外,类也可以是 callable 对象,只要实现了__call__ 函数(上面几个盒子已经接触过了),还有比较少人使用的偏函数也是 callable 对象。u003Cu002Fpu003Eu003Cpu003E接下来就来说说,如何使用 类和偏函数结合实现一个与众不同的装饰器。u003Cu002Fpu003Eu003Cpu003E如下所示,DelayFunc 是一个实现了 __call__ 的类,delay 返回一个偏函数,在这里 delay 就可以做为一个装饰器。(以下代码摘自 Python工匠:使用装饰器的小技巧)u003Cu002Fpu003Eu003Cpreu003Eimport timeu003Cbru003Eimport functoolsu003Cbru003E​u003Cbru003Eclass DelayFunc:u003Cbru003E def __init__(self, duration, func):u003Cbru003E self.duration = durationu003Cbru003E self.func = funcu003Cbru003E​u003Cbru003E def __call__(self, *args, **kwargs):u003Cbru003E print(f’Wait for {self.duration} seconds…’)u003Cbru003E time.sleep(self.duration)u003Cbru003E return self.func(*args, **kwargs)u003Cbru003E​u003Cbru003E def eager_call(self, *args, **kwargs):u003Cbru003E print(‘Call without delay’)u003Cbru003E return self.func(*args, **kwargs)u003Cbru003E​u003Cbru003Edef delay(duration):u003Cbru003E “””u003Cbru003E 装饰器:推迟某个函数的执行。u003Cbru003E 同时提供 .eager_call 方法立即执行u003Cbru003E “””u003Cbru003E # 此处为了避免定义额外函数,u003Cbru003E # 直接使用 functools.partial 帮助构造 # DelayFunc 实例u003Cbru003E return functools.partial(DelayFunc, duration)u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E我们的业务函数很简单,就是相加u003Cu002Fpu003Eu003Cpreu003E@delay(duration=2)u003Cbru003Edef add(a, b):u003Cbru003E return a+bu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E来看一下执行过程u003Cu002Fpu003Eu003Cpreu003E>>> add # 可见 add 变成了 Delay 的实例u003Cbru003E<__main__.DelayFunc object at 0x107bd0be0>u003Cbru003E>>> u003Cbru003E>>> add(3,5) # 直接调用实例,进入 __call__u003Cbru003EWait for 2 seconds…u003Cbru003E8u003Cbru003E>>> u003Cbru003E>>> add.func # 实现实例方法u003Cbru003E<function add at 0x107bef1e0>u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E09. 内置装饰器:propertyu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E以上,我们介绍的都是自定义的装饰器。u003Cu002Fpu003Eu003Cpu003E其实Python语言本身也有一些装饰器。比如property这个内建装饰器,我们再熟悉不过了。u003Cu002Fpu003Eu003Cpu003E它通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容。u003Cu002Fpu003Eu003Cpu003E通常我们给实例绑定属性是这样的u003Cu002Fpu003Eu003Cpreu003Eclass Student(object):u003Cbru003E def __init__(self, name, age=None):u003Cbru003E self.name = nameu003Cbru003E self.age = ageu003Cbru003E# 实例化u003Cbru003EXiaoMing = Student(“小明”)u003Cbru003E# 添加属性u003Cbru003EXiaoMing.age=25u003Cbru003E# 查询属性u003Cbru003EXiaoMing.ageu003Cbru003E# 删除属性u003Cbru003Edel XiaoMing.ageu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E但是稍有经验的开发人员,一下就可以看出,这样直接把属性暴露出去,虽然写起来很简单,但是并不能对属性的值做合法性限制。为了实现这个功能,我们可以这样写。u003Cu002Fpu003Eu003Cpreu003Eclass Student(object):u003Cbru003E def __init__(self, name):u003Cbru003E self.name = nameu003Cbru003E def set_age(self, age):u003Cbru003E if not isinstance(age, int):u003Cbru003E raise ValueError(‘输入不合法:年龄必须为数值!’)u003Cbru003E if not 0 < age < 100:u003Cbru003E raise ValueError(‘输入不合法:年龄范围必须0-100’)u003Cbru003E self._age=ageu003Cbru003E def get_age(self):u003Cbru003E return self._ageu003Cbru003E def del_age(self):u003Cbru003E self._age = Noneu003Cbru003EXiaoMing = Student(“小明”)u003Cbru003E# 添加属性u003Cbru003EXiaoMing.set_age(25)u003Cbru003E# 查询属性u003Cbru003EXiaoMing.get_age()u003Cbru003E# 删除属性u003Cbru003EXiaoMing.del_age()u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上面的代码设计虽然可以变量的定义,但是可以发现不管是获取还是赋值(通过函数)都和我们平时见到的不一样。 按照我们思维习惯应该是这样的。u003Cu002Fpu003Eu003Cpreu003E# 赋值u003Cbru003EXiaoMing.age = 25u003Cbru003E# 获取u003Cbru003EXiaoMing.ageu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E那么这样的方式我们如何实现呢。请看下面的代码。u003Cu002Fpu003Eu003Cpreu003Eclass Student(object):u003Cbru003E def __init__(self, name):u003Cbru003E self.name = nameu003Cbru003E self.name = Noneu003Cbru003E @propertyu003Cbru003E def age(self):u003Cbru003E return self._ageu003Cbru003E @age.setteru003Cbru003E def age(self, value):u003Cbru003E if not isinstance(value, int):u003Cbru003E raise ValueError(‘输入不合法:年龄必须为数值!’)u003Cbru003E if not 0 < value < 100:u003Cbru003E raise ValueError(‘输入不合法:年龄范围必须0-100′)u003Cbru003E self._age=valueu003Cbru003E @age.deleteru003Cbru003E def age(self):u003Cbru003E del self._ageu003Cbru003EXiaoMing = Student(“小明”)u003Cbru003E# 设置属性u003Cbru003EXiaoMing.age = 25u003Cbru003E# 查询属性u003Cbru003EXiaoMing.ageu003Cbru003E# 删除属性u003Cbru003Edel XiaoMing.ageu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。同时,会将这个函数变成另外一个装饰器。就像后面我们使用的@age.setter和@age.deleter。u003Cu002Fpu003Eu003Cpu003E@age.setter 使得我们可以使用XiaoMing.age = 25这样的方式直接赋值。 @age.deleter 使得我们可以使用del XiaoMing.age这样的方式来删除属性。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Ff8a5f28465374a958ca318180e01f5a8″ img_width=”800″ img_height=”600″ alt=”终于有人把python装饰器讲的明明白白的,太棒了” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003E10. 其他装饰器:装饰器实战u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E读完并理解了上面的内容,你可以说是Python高手了。别怀疑,因为很多人都不知道装饰器有这么多用法呢。u003Cu002Fpu003Eu003Cpu003E在小明看来,使用装饰器,可以达到如下目的:u003Cu002Fpu003Eu003Colu003Eu003Cliu003E使代码可读性更高,逼格更高;代码结构更加清晰,代码冗余度更低;u003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003E刚好我最近在写代码的时候也有一个场景,可以用装饰器很好的实现,暂且放上来看看。u003Cu002Fpu003Eu003Cpu003E这是一个实现控制函数运行超时的装饰器。如果超时,则会抛出超时异常。u003Cu002Fpu003Eu003Cpreu003Eimport signalu003Cbru003Eclass TimeoutException(Exception):u003Cbru003E def __init__(self, error=’Timeout waiting for response from Cloud’):u003Cbru003E Exception.__init__(self, error)u003Cbru003Edef timeout_limit(timeout_time):u003Cbru003E def wraps(func):u003Cbru003E def handler(signum, frame):u003Cbru003E raise TimeoutException()u003Cbru003E def deco(*args, **kwargs):u003Cbru003E signal.signal(signal.SIGALRM, handler)u003Cbru003E signal.alarm(timeout_time)u003Cbru003E func(*args, **kwargs)u003Cbru003E signal.alarm(0)u003Cbru003E return decou003Cbru003E return wrapsu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:终于有人把python装饰器讲的明明白白的,太棒了

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/12934.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code