装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。
其实在平常写写脚本的过程中,这个知识点你可能用到不多
但在面试的时候,这可是一个高频问题。
一、什么是装饰器
所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。
这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。
放心,绝对不是"Hello World"!
def hello(): print("你好,装饰器")
肿么样,木骗你吧? 哈哈,这个函数不用运行相信大家都知道输出结果:"你好,装饰器"。
那如果我想让hello()
函数再实现个其他功能,比如多打印一句话。
那么,可以这样"增强"一下:
def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper def hello(): print("你好,装饰器") hello = my_decorator(hello) hello()
运行结果:
这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]
很显然,这个"增强"没啥作用,但是可以帮助理解装饰器。
当运行最后的hello()
函数时,调用过程是这样的:
hello = my_decorator(hello)
中,变量hello指向的是my_decorator()
my_decorator(func)
中传参是hello
,返回的wrapper
,因此又会调用到原函数hello()
- 于是乎,先打印出了
wrapper()
函数里的,然后才打印出hello()
函数里的
那上述代码里的my_decorator()
就是一个装饰器。
它改变了hello()
的行为,但是并没有去真正的改变hello()
函数的内部实现。
但是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。
二、优雅的装饰器
所以,想让上述装饰器变得优雅,可以这样写:
def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") hello()
这里的@my_decorator
就相当于旧代码的hello = my_decorator(hello)
,@符号称为语法糖。
那如果还有其他函数也需要加上类似的装饰,直接在函数的上方加上@my_decorator
就可以,大大提高函数
的重复利用与可读性。
def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") @my_decorator def hello2(): print("你好,装饰器2") hello2()
输出:
这是装饰后具有的新输出
你好,装饰器2
[Finished in 0.1s]
三、带参数的装饰器
1. 单个参数
上面的只是一个非常简单的装饰器,但是实际场景中,很多函数都是要带有参数的,比如hello(people_name)。
其实也很简单,要什么我们就给什么呗,直接在对应装饰器的wrapper()
上,加上对应的参数:
def my_decorator(func): def wrapper(people_name): print("这是装饰后具有的新输出") func(people_name) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) hello("张三")
输出:
这是装饰后具有的新输出
你好,张三
[Finished in 0.1s]
2. 多个参数
但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的,
当其他要使用装饰器的函数参数不止这个一个肿么办?比如:
@my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener))
没关系,在python里,*args
和**kwargs
表示接受任意数量和类型的参数,所以我们可以这样
写装饰器里的wrapper()
函数:
def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) @my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener)) hello("老王") print("------------------------") hello3("张三", "李四")
同时运行下hello("老王")
,和hello3("张三", "李四")
,看结果:
这是装饰后具有的新输出
你好,老王
------------------------
这是装饰后具有的新输出
张三对李四说你好!
[Finished in 0.1s]
3. 自定义参数
上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。
比如,我加个参数来控制下装饰器中打印信息的次数:
def count(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper return my_decorator @count(3) def hello(people_name): print("你好,{}".format(people_name)) hello("老王")
注意,这里count
装饰函数中的2个return
.
运行下,应该会出现3次:
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
[Finished in 0.1s]
4. 内置装饰器@functools.wrap
现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:
def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__) #看下hello函数的元信息
输出:
wrapper
这说明了,它不再是以前的那个 hello()
函数,而是被 wrapper()
函数取代了。
如果我们需要用到元函数信息,那怎么保留它呢?这时候可以用内置装饰器@functools.wrap
。
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__)
运行下:
hello
[Finished in 0.1s]
四、类装饰器
装饰器除了是函数之外,也可以是类。
但是类作为装饰器的话,需要依赖一个函数__call__(),当调用这个类的实例时,函数__call__()就
会被执行。
来改造下之前的例子,把函数装饰器改成类装饰器:
class MyDecorator(): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("这是装饰后具有的新输出") return self.func(*args, **kwargs) # def my_decorator(func): # def wrapper(): # print("这是装饰后具有的新输出") # func() # return wrapper @MyDecorator def hello(): print("你好,装饰器") hello()
运行:
这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]
跟函数装饰器一样,实现一样的功能。
五、装饰器的嵌套
既然装饰器可以增强函数的功能,那如果有多个装饰器,我都想要怎么办?
其实,只要把需要用的装饰器都加上去就好了:
@decorator1 @decorator2 @decorator3 def hello(): ...
但是要注意这里的执行顺序,会从上到下去执行,可以来看下:
def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper def my_decorator2(func): def wrapper(): print("这是装饰后具有的新输出2") func() return wrapper def my_decorator3(func): def wrapper(): print("这是装饰后具有的新输出3") func() return wrapper @my_decorator @my_decorator2 @my_decorator3 def hello(): print("你好,装饰器") hello()
运行
这是装饰后具有的新输出
这是装饰后具有的新输出2
这是装饰后具有的新输出3
你好,装饰器
[Finished in 0.1s]
好记性不如烂笔头,写一下理解一下会好很多。
以上就是通俗讲解python 装饰器的详细内容,更多关于python 装饰器的资料请关注其它相关文章!
python,装饰器
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]