python class用法理解_通过钢铁侠变身快速理解Python的装饰器用法
1 一切都要从函数说起
我们都知道一个函数可以返回一些数据,然后这些数据可以被其他函数调用。函数里还可以有若干个参数,可以让函数根据不同的输入值进行不同的计算,然后得到新的结果。
于是,我们的故事就可以开始了。
现有第一个函数,tony,很简单,直接返回tony,然后你叫他一声。他就回应你:tony。
def tony():return 'tony'
tony()
'tony'
紧接着,我们来到第二个函数,他可以根据传入的一个男人man,然后说出谁是钢铁侠。
def iron_man(man):print(f'{man} is iron man!')
由于在Python里面任何东西都是个对象,函数也不例外,我们就直接把上面的tony()
(注意括号啊!)直接传给iron_man
,会得到什么呢?
如你所见,结果很简单,当做参数的函数,返回值传入了新的函数,然后组合出现了我们看到的结果。
iron_man(tony())
tony is iron man!
刚才只是简单的说一下谁是钢铁侠,下面他就要变身了。
既然函数可以当做参数,那么我们在定义复杂一点的函数。如下所示,把函数名func当做参数,然后嵌套写一个包装函数 wrapper,这个函数里面调用func,这时候是有括号的,当做他的变身。
同时这行代码的上下,分别添加语句,分别显示变身前和变身后。
这个函数的最后,返回的是内部包裹的函数名,其实就是个函数对象,但是,没有调用呢。
# tony要变身def deco_iron(func):def wrapper():print('开始准备变身')man = func()print(f'{man} 正在变身!')print('变身结束!')return wrapper
接下来,我们调用这个复杂函数,就可以完成钢铁侠的变身了。和刚才不同的是,注意我们只是传入函数名。因为这个函数要在刚才包裹他的函数内部运行。
我们可以给个新的变量new_tony,注意咯,这时候得到的是刚才复杂函数的返回值,也就是内部的函数对象。如果要调用,需要加括号的。
new_tony = deco_iron(tony) # 注意!!没有括号,去变身里面执行
试一下就知道了。
new_tony()
开始准备变身tony 正在变身!变身结束!
如你所见,变身成功。下面问题来了:有没有可能直接运行tony就达到效果?
当然有,我们把刚才的返回值变量直接写作tony,运行看看:
tony = deco_iron(tony)tony()
开始准备变身tony 正在变身!变身结束!
回顾一下,我们现在相当于还是调用名为tony的函数,但是,在没有改变原来函数的情况下,我们给他添加了整套的变身语句。如同钢铁盔甲增强了tony的身体素质,我们用了一个嵌套函数增强了刚才简简单单的tony函数,但其实从头到尾没有修改原来的tony函数。这就给代码增添了无限的可能。
但,这样调用来调用去的有点啰嗦,需要简化。
2 装饰器@魔法出现了
Python是一个以简洁著称的语言,刚才那种写完嵌套函数,调用一下,又返回同样的函数名,然后等着再调用的做法,直接可以用一个@
符号搞定。
这时候的@
相当于tony = deco_iron(tony)
,然后直接加到原来的函数上头就ok了。如下所示:
@deco_irondef tony():return 'tony'
调用一下这个函数,你会发现已经被装饰加强了。这就是装饰器了。
tony()
开始准备变身tony 正在变身!变身结束!
3 装饰器的变身
传递参数
函数是可以传递参数的,如果我们想让被装饰的函数传递参数可以这样做:
正常添加任何参数,比如我们这里加个msg
在装饰器函数的内部函数中,使用
*args, **kw
接收任何参数就ok了
为了刚方便查看测试效果,我们这里再用func.__name__
格式化输出一下函数名。
# 传递参数def deco_iron(func):print('deco_iron开始运行....')def wrapper(*args, **kw):print('开始变身,在执行{}函数前'.format(func.__name__))print(args, kw)func(*args, **kw)print('钢铁侠变身完成,结束{}函数'.format(func.__name__))return wrapper
下面,从新装饰一下tony函数,你会发现,定义之后就会自动运行上面装饰器函数的第一句话。其实这就是在Python内部,自动完成了tony = deco_iron(tony)
,悄悄第一次运行了整个装饰器函数。接下来我们要调用一下下面的函数,才会运行外部函数的返回结果,从而进入的最内部的函数。
@deco_irondef tony(msg):print(f'tony说:{msg}')
deco_iron开始运行....
tony('我来啦!')
开始变身,在执行tony函数前('我来啦!',) {}tony说:我来啦!钢铁侠变身完成,结束tony函数
多重装饰器
单层的装饰器说完了。下面说一下多重装饰器。比如钢铁侠,有时候要去和更强大的敌人战斗,需要多穿一层盔甲。
于是,DE8UG新写个带new后缀的函数,同时在里面适当修改提示语句。
# 多重装饰器def deco_iron_new(func):print('deco_iron_new开始运行....')def wrapper(*args, **kw):print('deco_iron_new 开始变身,在执行{}函数前'.format(func.__name__))print(args, kw)func(*args, **kw)print('deco_iron_new 钢铁侠变身完成,结束{}函数'.format(func.__name__))return wrapper
接下来就是使用了。只需要在要装饰的函数上,依次往上叠加@装饰函数名,就ok。这时候,定义语句被执行时候,你会发现同样执行了装饰器里面的第一句说明。
看一下执行顺序,是按照装饰顺序,从内到外执行的。
@deco_iron_new@deco_irondef tony(msg):print(f'tony说:{msg}')
deco_iron开始运行....deco_iron_new开始运行....
但,我们需要注意的是,当实际执行tony函数时,会发生什么,先看结果再解释。
tony('我要超级变身')
deco_iron_new 开始变身,在执行wrapper函数前('我要超级变身',) {}开始变身,在执行tony函数前('我要超级变身',) {}tony说:我要超级变身钢铁侠变身完成,结束tony函数deco_iron_new 钢铁侠变身完成,结束wrapper函数
你会发现一个有意思的现象,实际执行的函数,是按照装饰的层次,从外到内执行了。
为什么呢?
仔细思考上文的装饰器语法含义,我们会发现刚才定义时,从内到外执行的多个装饰器函数,每个装饰器函数都返回一个函数对象。这时候最外层的返回函数对象当然就在最外面了。
当被装饰后的函数具体执行时,就像剥洋葱一样,从外到内,一层层的执行具体的语句。
发现函数名问题
相信眼尖的同学看出来了,刚才多层装饰器一次出现的说明语句,对于函数名的解析,似乎有点问题,最外层的没有正确说明要执行的tony函数。
这是为什么?
在函数执行的时候,会有自己的变量作用域,当多层嵌套的装饰器执行时,虽然运行不出错,但其实内部的作用域出现的混乱。比如我们看见的错误的显示了内嵌函数的名称。
这个怎么解决呢,Python自己带来的问题,当然可以自己解决。直接使用functools里的wraps装饰器,来修复这个作用域问题。用法就是在内嵌函数上装饰一下,记住原函数信息。
再次运行一下,你会发现函数名已经正常显示了。
from functools import wraps# 多重装饰器def deco_iron_new(func):print('deco_iron_new 开始运行....')@wraps(func)def wrapper(*args, **kw):print('deco_iron_new 开始变身,在执行{}函数前'.format(func.__name__))print(args, kw)func(*args, **kw)print('deco_iron_new 钢铁侠变身完成,结束{}函数'.format(func.__name__))return wrapper# 传递参数def deco_iron(func):print('deco_iron开始运行....')@wraps(func)def wrapper(*args, **kw):print('开始变身,在执行{}函数前'.format(func.__name__))print(args, kw)func(*args, **kw)print('钢铁侠变身完成,结束{}函数'.format(func.__name__))return wrapper
@deco_iron_new@deco_irondef tony(msg):print(f'tony说:{msg}')
deco_iron开始运行....deco_iron_new 开始运行....
tony('再次变身')
deco_iron_new 开始变身,在执行tony函数前('再次变身',) {}开始变身,在执行tony函数前('再次变身',) {}tony说:再次变身钢铁侠变身完成,结束tony函数deco_iron_new 钢铁侠变身完成,结束tony函数
如果装饰器加参数呢
DE8UG小提示:以后如何查询此文档以及获取其他帮助?
在手机上搜索「Python随身听」官方号,点击右上角「...」,进入主题页面,找到页面上方的放大镜?,点击放大镜?搜索关键词即可。
下面再来个复杂点的。既然装饰器本身还是函数,那么当然可以继续添加参数。
怎么加呢?
再嵌套一层函数,把参数传给它!
比如,钢铁侠要带着小辣椒一起飞,可以这么写:
# 如果装饰器加参数呢?def deco_params(name):def deco_iron(func):print('deco_iron开始运行....')@wraps(func)def wrapper(*args, **kw):print('新参数需要处理:', name)print('开始变身,在执行{}函数前'.format(func.__name__))func(*args, **kw)print('钢铁侠变身完成,结束{}函数'.format(func.__name__))return wrapperreturn deco_iron@deco_params('小辣椒')def tony(msg):print(f'tony说:{msg}')
deco_iron开始运行....
tony('hi, 小辣椒')
新参数需要处理:小辣椒开始变身,在执行tony函数前tony说:hi, 小辣椒钢铁侠变身完成,结束tony函数
4 装饰器的用途
Python中,装饰器的用途很多。我们举一个web应用的例子。
我们都知道一个网站往往会有多个连接,根据不同的连接地址,进行路由转发,可以显示不同的业务内容。于是,我们可以这样定义一个类MyApp,表示网络应用。
里面放置三个函数,完成路由转发的功能:
初始化函数,放置一个字典
注册函数,用来当其他业务函数的装饰器。这是个嵌套函数,里面可以根据不同的路由页面名称,把函数名保存到字典
实际页面调用函数。
接下来使用时,可以很方便把不同页面的响应函数,注册到不同的路由下面。当访问不同页面时,根据响应的参数直接调用同一个方法,就能自动去转换到对应的页面内容了。
# 装饰器,各种用途# author: De8uGclass MyApp():def __init__(self):self.func_map = {}def register(self, name):def func_wrapper(func):print('func_wrapper: ', name, func)self.func_map[name] = funcreturn funcreturn func_wrapperdef call_method(self, name=None):func = self.func_map.get(name, None)if func is None:raise Exception("No function registered against - " + str(name))return name, func()# 创建对象,用于添加方法进行调用app = MyApp()@app.register('/')def main_page_func():return "This is the main page."@app.register('/next_page')def next_page_func():return "This is the next page."print(app.call_method('/'))print(app.call_method('/next_page'))
func_wrapper: / func_wrapper: /next_page ('/', 'This is the main page.')('/next_page', 'This is the next page.')
ok,以上就是DE8UG给你带来的装饰器的相关知识了。你学会了吗?有任何问题,建议欢迎到「Python随身听」留言,感谢关注?。
看到这里的同学们有福利咯
无论你是点赞?,在看?,转发→,
还是留言?,赞赏?,
都会作为呈堂证供,
转化为后期DE8UG收费课程的相应积分,
无上限赠送
感谢你对Python随身听的支持
想查看更多?
请点击原文⬇️
python class用法理解_通过钢铁侠变身快速理解Python的装饰器用法相关推荐
- python装饰器用法_深入浅出分析Python装饰器用法
本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 用类作为装饰器 示例一 最初代码: class bol(object): def __init__(self, func): s ...
- python 装饰器 参数-python函数装饰器之带参数的函数和带参数的装饰器用法示例...
本文实例讲述了python函数装饰器之带参数的函数和带参数的装饰器用法.分享给大家供大家参考,具体如下: 1. 函数带多个参数 # 普通的装饰器, 打印函数的运行时间 def decrator(fun ...
- python装饰器函数-python函数装饰器之带参数的函数和带参数的装饰器用法示例
本文实例讲述了python函数装饰器之带参数的函数和带参数的装饰器用法.分享给大家供大家参考,具体如下: 1. 函数带多个参数 # 普通的装饰器, 打印函数的运行时间 def decrator(fun ...
- python装饰器实例-Python装饰器用法实例总结
本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能, ...
- python装饰器 property_python中property和setter装饰器用法
作用:调用方法改为调用对象, 比如 : p.set_name() 改为 p.set_name 区别:前者改变get方法,后者改变set方法 效果图: 代码: class Person: def __i ...
- python函数装饰器详解_Python语言函数装饰器用法实例详解
这篇文章主要介绍了Python语言函数装饰器用法,以实例形式较为详细的分析了Python函数装饰器的常见使用技巧,需要的朋友可以参考下,希望对大家学习Python语言有所帮助. 本文实例讲述了pyth ...
- 21天python百度网盘_《21天学通Python》PDF 高清版百度网盘下载
提取码:gr0z 内容简介 · · · · · · <21天学通Python>全面.系统.深入地讲解了Python编程基础语法与高级应用.在讲解过程中,通过大量实际操作的实例将Pytho ...
- python3.7用法_Python 3.7中dataclass装饰器用法详解
Python 3.7的dataclass装饰器用法 Python 3.7新功能之dataclass装饰器详解 前言 Python 3.7 将于今年夏天发布,Python 3.7 中将会有许多新东西: ...
- python释放变量内存_看完2019年阿里巴巴Python面试题详解,月薪3万不是梦
很多人想自学Python找工作,下面给大家分享一部分阿里巴巴的Python开发工程师的面试题目: 概念理解类题目: 1.请说一下你对迭代器和生成器的区别? 答:(1)迭代器是一个更抽象的概念,任何对象 ...
最新文章
- BIO 三位标注 (B-begin,I-inside,O-outside)
- [BX] 和 loop指令
- TensorFlow tf.data.Dataset
- java gc 可达性_JAVA--GC 垃圾回收机制----可达性分析算法
- c语言贪吃蛇源代码window32,Win32贪吃蛇源代码。背景非常简单
- 5999卖999!是噱头还是颠覆
- 【LeetCode】【数组】题号:414,第三大的数
- java客户端实验_java实验(客户端) 2015106宋世超
- TP5的类似TP3使用‘DEFAULT_THEME’的配置修改主题风格的方法,以及常见模板错误...
- 《数字图像处理》实验7
- c++使用librdkafka kerberos认证
- 在 linux ubuntu 18.04 上运行QQ音乐
- Git使用教程之初级入门命令行(二)
- android 虚拟按键自定义,Android手机底部栏虚拟按键的操作
- Ubuntu的ldconfig详解(解决*.so不是符号连接)
- 宝宝巴士儿歌下载链接
- 【修真院小课堂】JWT简单介绍
- 论Web Service 相关技术
- 软考不通过能不能补考?解答来了
- android bilibili弹幕技术解析,bilibili弹幕定位
热门文章
- php 环境优化,Nginx与PHP-fpm环境在大流量下的优化配置
- java split 路径,JAVA通过文件路径分隔符分割文件路径
- java 参数内存释放_JNI创建变量和释放变量
- php 处理tiff,TIFF图像文件(五):LZW的PHP应用
- java 特殊字符过滤器_java处理url中的特殊字符
- thymeleaf中的条件判断用法
- 改变support中AlertDialog的样式
- oracle怎么变为整数,如何在Oracle 11g SQL中为char添加整数?(How to add integers to char in Oracle 11g SQL?)...
- 中国2008经济数据
- jQuery-处理元素内容、表单元素