高考考试网
当前位置: 首页 高考资讯

python的八种装饰器(读懂系列一文读懂Python装饰器)

时间:2023-06-03 作者: 小编 阅读量: 2 栏目名: 高考资讯

但是,它也有很多较难掌握的高级功能,比如装饰器。很多初学者一直不理解装饰器及其工作原理,在这篇文章中,我们将介绍装饰器的来龙去脉。装饰器本质上也是一种函数,它可以让其它函数在不经过修改的情况下增加一些功能。一般而言,我们可以使用装饰器提供的@语法糖来修饰其它函数或对象。设置问题为了解装饰器的目的,接下来我们来看一个简单的示例。

选自pouannes.github.io

作者:Pierre Ouannes

编译:机器之心(almosthuman2014)

原文:https://pouannes.github.io/blog/decorators/

Python 是一种对新手很友好的语言。但是,它也有很多较难掌握的高级功能,比如装饰器(decorator)。很多初学者一直不理解装饰器及其工作原理,在这篇文章中,我们将介绍装饰器的来龙去脉。

在 Python 中,函数是一种非常灵活的结构,我们可以把它赋值给变量、当作参数传递给另一个函数,或者当成某个函数的输出。装饰器本质上也是一种函数,它可以让其它函数在不经过修改的情况下增加一些功能。

这也就是「装饰」的意义,这种「装饰」本身代表着一种功能,如果用它修饰不同的函数,那么也就是为这些函数增加这种功能。

一般而言,我们可以使用装饰器提供的 @ 语法糖(Syntactic Sugar)来修饰其它函数或对象。如下所示我们用 @dec 装饰器修饰函数 func :

@dec

def func:

pass

理解装饰器的最好方式是了解装饰器解决什么问题,本文将从具体问题出发一步步引出装饰器,并展示它的优雅与强大。

设置问题

为了解装饰器的目的,接下来我们来看一个简单的示例。假如你有一个简单的加法函数 dec.py,第二个参数的默认值为 10:

# dec.py

def add(x, y=10):

return xy

我们来更认真地看一下这个加法函数:

>>> add(10, 20)

30

>>> add

<function add at 0x7fce0da2fe18>

>>> add.__name__

add

>>> add.__module__

__main__

>>> add.__defaults__ # default value of the `add` function

(10,)

>>> add.__code__.co_varnames # the variable names of the `add` function

( x ,y )

我们无需理解这些都是什么,只需要记住 Python 中的每个函数都是对象,它们有各种属性和方法。你还可以通过 inspect 模块查看 add 函数的源代码:

>>> from inspect import getsource

>>> print(getsource(add))

def add(x, y=10):

return xy

现在你以某种方式使用该加法函数,比如你使用一些操作来测试该函数:

# dec.py

from time import time

def add(x, y=10):

return xy

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

Output: i

add(10) 20

add(20, 30) 50

add("a", "b") ab

假如你想了解每个操作的时间,可以调用 time 模块:

# dec.py

from time import time

def add(x, y=10):

return xy

before = time

print( add(10) , add(10))

after = time

print( time taken: , after - before)

before = time

print( add(20, 30) , add(20, 30))

after = time

print( time taken: , after - before)

before = time

print( add("a", "b") , add("a", "b"))

after = time

print( time taken: , after - before)

Output:

add(10) 20

time taken: 6.699562072753906e-05

add(20, 30) 50

time taken: 6.9141387939453125e-06

add("a", "b") ab

time taken: 6.9141387939453125e-06

现在,你作为一个编程人员是不是有些手痒,毕竟我们不喜欢总是复制粘贴相同的代码。现在的代码可读性不强,如果你想改变什么,你就得修改所有出现的地方,Python 肯定有更好的方式。

我们可以按照如下做法,直接在 add 函数中捕捉运行时间:

# dec.py

from time import time

def add(x, y=10):

before = time

rv = xy

after = time

print( time taken: , after - before)

return rv

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

这种方法肯定比前一种要好。但是如果你还有另一个函数,那么这似乎就不方便了。当我们有多个函数时:

# dec.py

from time import time

def add(x, y=10):

before = time

rv = xy

after = time

print( time taken: , after - before)

return rv

def sub(x, y=10):

return x - y

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

print( sub(10) , sub(10))

print( sub(20, 30) , sub(20, 30))

因为 add 和 sub 都是函数,我们可以利用这一点写一个 timer 函数。我们希望 timer 能计算一个函数的运算时间:

def timer(func, x, y=10):

before = time

rv = func(x, y)

after = time

print( time taken: , after - before)

return rv

这很不错,不过我们必须使用 timer 函数包装不同的函数,如下所示:

print( add(10) , timer(add,10)))

现在默认值还是 10 吗?未必。那么如何做得更好呢?

这里有一个主意:创建一个新的 timer 函数,并包装其他函数,然后返回包装后的函数:

def timer(func):

def f(x, y=10):

before = time

rv = func(x, y)

after = time

print( time taken: , after - before)

return rv

return f

现在,你只需用 timer 包装一下 add 和 sub 函数 :

add = timer(add)

这样就可以了!以下是完整代码:

# dec.py

from time import time

def timer(func):

def f(x, y=10):

before = time

rv = func(x, y)

after = time

print( time taken: , after - before)

return rv

return f

def add(x, y=10):

return xy

add = timer(add)

def sub(x, y=10):

return x - y

sub = timer(sub)

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

print( sub(10) , sub(10))

print( sub(20, 30) , sub(20, 30))

Output:

time taken: 0.0

add(10) 20

time taken: 9.5367431640625e-07

add(20, 30) 50

time taken: 0.0

add("a", "b") ab

time taken: 9.5367431640625e-07

sub(10) 0

time taken: 9.5367431640625e-07

sub(20, 30) -10

我们来总结一下这个过程:我们有一个函数(比如 add 函数),然后用一个动作(比如计时)包装该函数。包装的结果是一个新函数,能实现某些新功能。

当然了,默认值还有点问题,稍后我们会解决它。

装饰器

现在,上面的解决方案以及非常接近装饰器的思想了,使用常见行为包装某个具体的函数,这种模式就是装饰器在做的事。使用装饰器后的代码是:

def add(x, y=10):

return xy

add = timer(add)

You write:

@timer

def add(x, y=10):

return xy

它们的作用是一样的,这就是 Python 装饰器的作用。它实现的作用类似于 add = timer(add),只不过装饰器把句法放在函数上面,且句法更加简单:@timer。

# dec.py

from time import time

def timer(func):

def f(x, y=10):

before = time

rv = func(x, y)

after = time

print( time taken: , after - before)

return rv

return f

@timer

def add(x, y=10):

return xy

@timer

def sub(x, y=10):

return x - y

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

print( sub(10) , sub(10))

print( sub(20, 30) , sub(20, 30))

参数和关键字参数

现在,还有一个小问题没有解决。在 timer 函数中,我们将参数 x 和 y 写死了,即指定 y 的默认值为 10。有一种方法可以传输该函数的参数和关键字参数,即 *args 和 **kwargs。参数是函数的标准参数(在本例中 x 为参数),关键字参数是已具备默认值的参数(本例中是 y=10)。代码如下:

# dec.py

from time import time

def timer(func):

def f(*args, **kwargs):

before = time

rv = func(*args, **kwargs)

after = time

print( time taken: , after - before)

return rv

return f

@timer

def add(x, y=10):

return xy

@timer

def sub(x, y=10):

return x - y

print( add(10) , add(10))

print( add(20, 30) , add(20, 30))

print( add("a", "b") , add("a", "b"))

print( sub(10) , sub(10))

print( sub(20, 30) , sub(20, 30))

现在,该 timer 函数可以处理任意函数、任意参数和任意默认值设置了,因为它仅仅将这些参数传输到函数中。

高阶装饰器

你们可能会疑惑:如果我们可以用一个函数包装另一个函数来添加有用的行为,那么我们可以再进一步吗?我们用一个函数包装另一个函数,再被另一个函数包装吗?

可以!事实上,函数的深度可以随你的意。例如,你想写一个装饰器来执行某个函数 n 次。如下所示:

def ntimes(n):

def inner(f):

def wrapper(*args, **kwargs):

for _ in range(n):

rv = f(*args, **kwargs)

return rv

return wrapper

return inner

然后你可以使用上述函数包装另一个函数,例如前文中的 add 函数:

@ntimes(3)

def add(x, y):

print(xy)

return xy

输出的语句表明该代码确实执行了 3 次。

,
    推荐阅读
  • 迁坟时女人不能去(迁坟时女人不能去的原因)

    尤其山里的居民,老人去世后多数埋在山上。这些坟地离家远不说,而且前往坟地的道路也不好走。迁坟时孕妇前往现场,不但对孕妇有不利影响,严重者还会累及腹中的胎儿,因此不建议孕妇前往迁坟现场。另一方面迁坟时辰多在凌晨,而这个时间段坟地阴气重,对女性身体不利。因此,迁坟时需要有全福的人或身体强壮的人在场。

  • 七夕爱情歌会湖南台(湖南卫视爱情歌会七夕节现场直播)

    《2018快乐中国·爱情歌会》将于8月17日七夕节当晚19:30进行现场直播。晚会阵容片已于8月13日震撼首发,众多嘉宾闪亮登场,晚会环节更具看点。阵容嘉宾海报也同时在陆续发布,其中三大亮点值得期待。原创晚会爱情主题曲首发尚雯婕甜蜜演绎精彩令人期待本次《2018快乐中国·爱情歌会》的主题曲《星城热恋》近日全网发布。届时,舞台上将不断切换不同追爱情景当中的场景,男嘉宾和女嘉宾在不同场景中分别献唱,同时互动表演。

  • 蟹炒年糕怎么烧(蟹炒年糕如何烧)

    蟹炒年糕怎么烧材料:螃蟹1只、年糕2条。调料:色拉油适量、食盐4克、葱2根、姜3片、蒜5克、淀粉少许、白糖2克、酱适量。螃蟹用筷子由头部中间插进去,5分钟后便会死亡,清洗干净后,切成块。取一个盘子放入适量生粉备用;将蟹块均匀的沾上一层生粉。锅中放油烧热后放入蟹块姜丝蒜片翻炒至8成熟后,调入盐,酱油,黄酒,一点点糖调鲜,放入年糕继续翻炒至成熟,临出锅时放入葱段翻匀即可。

  • 板栗的懒人做法(十一月板栗笑哈哈)

    听到这个可爱的谜语,哈妹就想到了板栗。板栗的营养十分丰富,而且具有干果之王的美誉,这是因为板栗之中含有大量人体所需要的多种营养素,例如淀粉、蛋白质以及脂肪、维生素等等的含量是非常客观的。十月板栗笑哈哈,板栗和排骨这做法太馋人,咬一口再难放下筷子,跟着哈妹一起看看美味的腐乳板栗烧排骨的具体烹饪步骤,老少皆宜,做法简单!

  • 有长期的网络兼职吗(一些靠谱的网络兼职)

    注意是兼职不是主业1、阿里巴巴云客服报名方式自己百度。报名成功后会短信通知,然后注册钉钉会有人拉你进群,发送一些培训资料,几天后进行考核。答题的时候要注意题目和特别注释。最后一定要注意的是,无论提出什么要求让你支付押金或者其他需要花钱,都是骗子。

  • DCT双离合变速器是干式还是湿式(DCT双离合变速器介绍)

    DCT双离合变速器是干式还是湿式DCT双离合变速器干式的和湿式的都有,双离合变速箱分为干式双离合变速箱和湿式双离合变速箱。无论干式还是湿式的传动原理都基本相同,都是有两个离合器分别轮流工作。湿式双离合器片,是指离合器片浸泡在变速箱油液里,而干式双离合的离合器片直接与发动机飞轮接触。从技术成熟度与可靠性上论,湿式双离合由于离合器片工作在液压油中,油液起到了一定的散热效果,所以使用起来更为稳定。

  • 张一山个人资料(中国内地男演员——张一山)

    张一山,1992年5月5日出生于北京市西城区,毕业于北京电影学院,中国内地男演员。2000年,张一山开始拍摄广告片,后被导演徐耿相中,出演首部电视剧作品《小兵张嘎》。2004年,因在少儿题材的情景喜剧《家有儿女》中饰演刘星后而被观众们熟知。张一山的表演是电视剧《余罪》中的亮点,他的表演不会不痛不痒也不会歇斯底里,而是与角色合为一体。张一山的演技好就好在真实,他能让人相信他演的角色就是他自己。

  • 草履虫是什么意思(关于草履虫的意思)

    草履虫是什么意思草履虫是一种身体很小,圆筒形的原生动物,它只由一个细胞构成,是单细胞动物,雌雄同体。最常见的是尾草履虫。体长只有180至280微米。草履虫属于动物界中最原始,最低等的原生动物。据估计,一只草履虫每小时大约能形成60个食物泡,每个食物泡中大约含有30个细菌,所以,一只草履虫每天大约能吞食43200个细菌,它对污水有一定的净化作用。

  • 高考前父母的寄语(高考前父母的寄语有什么)

    三年的生活和学习,你付出了不懈的努力和勤奋的汗水,也磨炼了坚强的意志,树立了坚定的信心,收获了扎实的知识。爸妈为你的成长和进步感到无比的欣慰和自豪。在高考来临之际,爸妈希望你保持积极乐观的心态,快乐迎接高考。发挥才智更努力,鱼跃龙门终如愿。高考临近,更要注意自己的饮食起居,确保积极向上的状态备战高考。高考只是人生的一个阶段性的检验,它绝不是对一个人“一锤定音”的评断。

  • 电梯场合如何消毒(电梯场合消毒方法)

    下面内容希望能帮助到你,我们来一起看看吧!电梯场合如何消毒电梯按键、轿厢扶手等表面可贴膜保护,在保护膜上用75%乙醇消毒剂或者浓度为250-500mg/L的含氯消毒剂擦拭,每两个小时一次,并做好消毒标识。贴膜每2天更换一次或发现破损及时更换。为配合做好疫情防控工作,保障人民群众安全乘用电梯,加强自我防范、减少乘梯频次、安全有序乘梯、遇事不要慌乱。电梯使用、维保单位:做好清洁消毒、加强日常巡查、配备充足应急救援力量。