装饰器 r囧r小猫 2022-05-14 16:43 253阅读 0赞 ### 文章目录 ### * 前言 * 一个示例 * 装饰器的作用 * 如何实现一个装饰器 * 装饰器传参 * 类装饰器 * 保留原函数的元数据 * 最后 # 前言 # 最近阅读公司的项目代码,许多地方用到了装饰器。然后就想起年初,那个时候刚刚接触Python,很多东西跟C啊C++不同。一时之间也没搞懂装饰器,跟着示例代码敲过几遍,也成功运行了,然后就像一个“渣男”头也不回的走掉。 真的就是走掉了,我从来没在自己的demo中使用装饰器。既然公司的项目需要,我当然不能说:“对不起不会喔。” 翻出之前做的笔记,居然发现也不难,就一下子会(简单使用 【 如果我不这样说明,万一惹到哪个大神不开森要为难我怎么办 】 )了。 总之,我觉得学习编程(应该也可以扩展到其他方面)不应该死磕,可能暂时领悟不了,那是因为你沉淀得还不够。就像小学时我因为不能背出乘法口诀表而被留堂,最后在同学的帮助下作弊通关,可现在,或许我数学不好,但至少不会不好在“背不出乘法口诀表”上了。 # 一个示例 # 昨天公司开周会,研发部所有人出席。我刚到公司不久,需要自我介绍,于是: def introduce(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": def introduce("Guan") # 输出: My name is Guan. 这时候老总说不行,你得说你在哪个组,还要说自己做什么呀!OKOK~ def position(func): def wrapper(*args, **kwargs): print("I am in the BMS Group and work on coding by Python.") result = func(*args, **kwargs) return result return wrapper @position def introduce(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce("Guan") # 输出: I am in the BMS Group and work on coding by Python. My name is Guan. “停停停。”老总仍不满意:“得说你有没有女朋友呀,我们这里单身女职员很多呢。” 那好吧: def single(func): def wrapper(*args, **kwargs): print("There is a single man who loves the male.") result = func(*args, **kwargs) return result return wrapper def position(func): def wrapper(*args, **kwargs): print("I am in the BMS Group and work on coding by Python.") result = func(*args, **kwargs) return result return wrapper @single @position def introduce(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce("Guan") # 输出: There is a single man who loves the male. I am in the BMS Group and work on coding by Python. My name is Guan. # 装饰器的作用 # 从前面的示例中,我们可以看出:装饰器能在不改变原函数结构的情况下,增添函数的功能。以及装饰器是**从上而下**的执行顺序(这一点很重要)。 三个代码片都只执行了`introduce("Guan")`,却输出三次不同的结果。这就是装饰器的意义所在,它可以让你的代码更优雅,再优雅。当然优雅并不是它的终极目的。 **首先**,装饰器可以让你的代码高度复用。在每个需要额外介绍职位和说明自己单身的地方,都可以加上装饰器(@single@position)。 **其次**,装饰器能让你专注地去实现函数的功能(一个函数最好只做一件事儿,而且是它该做的事儿)。 **最后**,如果你是在维护公司的项目代码,在不破坏原函数封装的同时需要添加额外功能,你应该优先考虑这个功能可不可以通过装饰器实现。 # 如何实现一个装饰器 # 到这一步的时候,网上许多文章就要开始说“Python中函数也是一个对象”,balabala。这样说当然没错啦,而且确实应该补充这个知识点,由外向内地去探索装饰器。只不过—— 当然仅仅是我的看法,也是我个人学习装饰器的一个经历。我明明是来学习装饰器的,沿路却一直不是我想看的风景。不看了,右上角左键,“哒”。关掉浏览器。 我是很介意也不认可一上来就是各种概念,挠得人头晕目眩。 -------------------- 在前面的第二个代码中: def position(func): def wrapper(*args, **kwargs): print("I am in the BMS Group and work on coding by Python.") result = func(*args, **kwargs) return result return wrapper @position def introduce(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce("Guan") position()函数中还有一个函数wrapper(),并且position()函数返回的是它内部函数的函数名wrapper。这是一个固定写法,大家可以记住。接下来说说他们参数的意义。 position()中的func接收的就是**被修饰函数的函数名**,也就是这里的"introduce"(当然不一定要用func,形参名可以自己设计)。而函数wrapper()中的\*args,\*\*kwargs表示任意多个参数或关键字参数,它们用来接收**调用introduce()时传入的实参**。在这里,你可以简单的认为,就是name。有了下面这张图,二者的关系就可以一一对应上了。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzU5MDUx_size_27_color_FFFFFF_t_70] 需要注意的是,里层函数return出来的结果,其实就是我们需要调用函数的结果。如果原函数没有返回值,可以不写。也可以写,因为Python中函数默认返回None。 # 装饰器传参 # 如果是一个女实习生呢?如果也需要她说明单身否,还可以用前面定义的装饰器@single吗?显然不可以,因为她不是“a single man”而是“woman”。这种时候,我们希望装饰器可以通过传参来实现男女通用。 def single(gender="man"): def decorator(func): def wrapper(*args, **kwargs): print("There is a single {gender}...".format(gender=gender)) result = func(*args, **kwargs) return result return wrapper return decorator @single("woman") def introduce_first(name): print("My name is {name}.".format(name=name)) @single("man") def introduce_second(name): print("My name is {name}.".format(name=name)) @single() def introduce_third(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce_first("Ying") introduce_second("Guan") introduce_third("Li") # 输出: There is a single woman... My name is Ying. There is a single man... My name is Guan. There is a single man... My name is Li. 需要注意,这里不再是简单的函数嵌套函数,而是函数嵌套函数再嵌套一个函数。欸?为什么要这样写了呢?好了,得讲一波原理了。 -------------------- 当我们实现一个@single装饰器,用它来装饰一个函数时: @single def introduece(name): ... introduce = single(introduce) 这意思是,将原来的函数扔给了single包装,之后再“吐”出一个introduce,而我们之后调用的introduce就是“吐”来的那个。当装饰器需要传参的时候: @single("woman") def introduce(): ... introduce = single("woman")(introduce) 装饰器会先接收它自己的参数(woman),然后返回一个函数,这个函数再接收被装饰的函数(introduce),最后返回一个introduce。这个流程,我应该有阐述清楚吧! 这也就是上面嵌套了三层函数的原因。既然搞懂了这个过程,我们当然也可以用两层嵌套来实现,但需要借助偏函数partial。 from functools import partial def single(func=None, gender=None): if func is None: return partial(single, gender=gender) def wrapper(*args, **kwargs): print("There is a single {gender}...".format(gender=gender)) result = func(*args, **kwargs) return result return wrapper @single(gender="man") def introduce(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce("Guan") **在两层嵌套中实现装饰器传参时,一定要关键字传参**,如果位置传参会让func来接收man,但我们的意图很明显,希望func接收的是introduce。 所以第一次传入man时,func一定为None,之后会进入if的执行语句中,返回一个绑定了gender参数的single。接下来会调用这个绑定了参数的single,传入的参数是introduce,传入的方式是位置传参。 -------------------- 好了,现在我们可以装饰器传参了。虽然三层嵌套实现的装饰器不必关键字传参,但它也不能通过@single来装饰一个函数,而必须\*\*@single()\*\*。尽管两层嵌套需要关键字传入,却可以直接使用@single。 from functools import partial def single(func=None, gender="man"): if func is None: return partial(single, gender=gender) def wrapper(*args, **kwargs): print("There is a single {gender}...".format(gender=gender)) result = func(*args, **kwargs) return result return wrapper @single(gender="woman") def introduce_first(name): print("My name is {name}.".format(name=name)) @single(gender="man") def introduce_second(name): print("My name is {name}.".format(name=name)) @single def introduce_third(name): print("My name is {name}.".format(name=name)) if __name__ == "__main__": introduce_first("Ying") introduce_second("Guan") introduce_third("Li") # 输出: There is a single woman... My name is Ying. There is a single man... My name is Guan. There is a single man... My name is Li. # 类装饰器 # 同样,把一个类构建成装饰器,在Python中也是允许的。 import types class Single(object): def __init__(self, func): self._func = func def __call__(self, *args, **kwargs): print("There is ...") self._func(*args, **kwargs) def __get__(self, instance, owner): if instance is None: return self else: # 手动创建一个绑定方法 return types.MethodType(self, self) @Single def introduce(name): print("My name is {name}".format(name=name)) class TempClass(object): @Single def introduce(self, name): print("My name is {name}".format(name=name)) if __name__ == "__main__": introduce("Guan") tempClass = TempClass() tempClass.introduce("Ying") # 输出: There is ... My name is Guan There is ... My name is Ying 同时实现\_\_call\_\_和\_\_get\_\_魔法方法的好处是:@Single不但可以装饰普通函数,也可以装饰类方法。 # 保留原函数的元数据 # 回到最开始的第二个代码,这一次我增加了注释: def position(func): @wraps(func) def wrapper(*args, **kwargs): print("I am in the BMS Group and work on coding by Python.") result = func(*args, **kwargs) return result return wrapper @position def introduce(name): """introduce yourself""" print("My name is {name}.".format(name=name)) 现在我们执行下面的语句: print(introduce.__name__) print(introduce.__doc__) # 输出: wrapper None 可以看到,函数名居然不是introduce,也没有文档信息了。但可能有的时候我们需要用到这一类数据,可以借助functools中的wraps来保留: from functools import wraps def position(func): @wraps(func) def wrapper(*args, **kwargs): ... return wrapper @position def introduce(name): """introduce yourself""" print("My name is {name}.".format(name=name)) if __name__ == "__main__": print(introduce.__name__) print(introduce.__doc__) # 输出: introduce introduce yourself # 最后 # * 装饰器的常用操作,以上就差不多了。但篇幅有限,没能详尽。这里极推《Python Cookbook》,Python进阶学习的不二之选。书厚且贵,网上有免费中文版([https://python3-cookbook.readthedocs.io/zh\_CN/latest/preface.html)。][https_python3-cookbook.readthedocs.io_zh_CN_latest_preface.html] * 原理没怎么讲,但网上随便找一篇都可以看到。所以不在这里重复造轮子了。 * 装饰器真的很优雅,难道你会不喜欢前面那个棉花糖吗? [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzU5MDUx_size_27_color_FFFFFF_t_70]: /images/20220503/042867af9df945d882453a4a5e5e3d16.png [https_python3-cookbook.readthedocs.io_zh_CN_latest_preface.html]: https://python3-cookbook.readthedocs.io/zh_CN/latest/preface.html%EF%BC%89%E3%80%82
相关 装饰器 装饰器 定义: 用来装饰的工具 在不改变源代码及调用方式的基础上额外增加新的功能 开放封闭原则: 扩展是开放的(增加新功能) 修改源码是封闭的(修改已经实现 柔光的暖阳◎/ 2023年10月10日 10:16/ 0 赞/ 60 阅读
相关 装饰器 [https://www.cnblogs.com/cicaday/p/python-decorator.html][https_www.cnblogs.com_cicaday_ ╰半橙微兮°/ 2023年01月06日 01:24/ 0 赞/ 142 阅读
相关 【python】闭包和装饰器、通用装饰器、多个装饰器、带参装饰器、类装饰器 1、闭包 概念: 把使用了外部函数变量的内部函数称为闭包 构成闭包的三个条件: 在函数嵌套的基础上(在外部函数内定义一个内部函数) 内部函数使用外部函数变 - 日理万妓/ 2022年11月28日 13:44/ 0 赞/ 252 阅读
相关 装饰器 装饰器 装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。 装饰器 ╰+攻爆jí腚メ/ 2022年06月07日 11:58/ 0 赞/ 244 阅读
相关 装饰器 以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这 港控/mmm°/ 2022年05月28日 13:27/ 0 赞/ 251 阅读
相关 装饰器 装饰器 如果我们要增强函数的功能,但又不希望修改`原`函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator); 本质上,de 墨蓝/ 2021年12月14日 14:17/ 0 赞/ 262 阅读
相关 装饰器 目录 普通装饰器 带参数的装饰器 多个装饰器装饰一个函数 装饰器的修复技术 装饰器 :在不改变原函数的代号和调用方法的基础上, 给原函数额外增加功能 女爷i/ 2021年10月29日 16:26/ 0 赞/ 377 阅读
相关 装饰器 装饰器的固定模式 def warpper(f): def inner(args,kwargs): print("WDNMD") 我就是我/ 2021年10月29日 07:12/ 0 赞/ 503 阅读
相关 装饰器 定义: 器字代表函数,装饰器本质是函数;装饰:装饰其他函数,就是为其他函数添加附加功能 原则: 1.不能修改被装饰函数的源代码(在不修改被装饰函数源代码的情况下为 心已赠人/ 2021年09月20日 12:00/ 0 赞/ 388 阅读
还没有评论,来说两句吧...