当前位置:
首页 > Python基础教程 >
-
python基础教程之Decorator——Python初级函数装饰器
最近想整一整数据分析,在看一本关于数据分析的书中提到了(1)if __name__ == '__main__' (2)列表解析式 (3)装饰器。
先简单描述一下前两点,再详细解说Python初级的函数装饰器。
进入正题:
一、if __name__ == '__main__'
- 首先,__name__是一个程序名变量,而这个变量的值是根据程序的运行方式决定的。如果程序是被当作主程序运行的,那__name__将会被赋值为__main__;当程序是作为模块被其他文件调用的,那它会自动被赋值为模块所在(程序)的文件名。
-
一般这句代码以这种形式出现:
1 def printHello(): 2 print("Hello World!") 3 print(__name__) 4 if __name__ == '__main__': 5 printHello()
其输出如下:
Hello World! __main__
因为此时这个文件是以主程序的方式运行的,故它的__name__为__main__。
- 这个 if 判断语句存在的意义在于当程序被作为模块引入其他文件时,一句import会使得这个模块自动运行一次。
-
故当 printHello 函数被作为模块调入其他程序文件时,即:
1 from name_main import printHello 2 printHello()
我们只会得到 printHello 运行一次的结果。这一次结果是 import 语句的效果,真正的 printHello 函数被 if 语句拦住了(因为它的__name__变成了自己的文件名而非__main__)。
二、列表解析式
- 列表解析式其实就是一种根据已有列表,高效创建新列表的方式。
- 通常形式如:a = [ i for i in list1 if i%2==0 ]
- 学会使用,可以简洁化代码。
三、装饰器
- 装饰器器如其名,起到了“装饰”的作用。体现在代码中就是给原程序添加“装饰”,添加新的东西。装饰器的本质是函数。
- 装饰器一般用于装饰函数和类。
- 有人问既然装饰器起到的是装饰的作用,那为什么不在函数中就添加这些“装饰”?当然可以,这确实可行。但设想我们需要每个函数打印自己的运行时间,那我们就需要在每一个函数里记录开始与结束时间,再相减得到时间差(即运行时间)。这会使得函数非常的“臃肿”,而且如果有成百上千个函数都需要这个功能,那一个一个函数加代码的措施就显得十分“无意义”。
- 但通过装饰器:我们简单地定义一个“装饰”函数去实现需要的功能,然后在执行函数的前面加上一句“@装饰函数”,就实现了对函数们的装饰和升级。
-
举一个例子,我们现在有两个函数分别实现打印 hello 和 goodbye,我们需要每次打印时同时把这个函数名打印出来。我们就可以用装饰器来完成,上代码。
#直接装饰@ def printname(func): #装饰器:实质是一个函数,参数是需要装饰的函数名(非函数调用) def wrapper(*args, **kwargs): #可变参数*args和关键字参数**kwargs print(f"[DEBUG]: enter {func.__name__}()") return func(*args, **kwargs) return wrapper #装饰器函数返回的是装饰完的函数 @printname def say_hello(): print("hello!") @printname def say_goodbye(): print("goodbye!") if __name__ == '__main__': say_hello() say_goodbye()
程序运行结果为:
[DEBUG]: enter say_hello() hello! [DEBUG]: enter say_goodbye() goodbye!
成功实现每次打印hello时打印出函数名称say_hello()。
-
接下来是对装饰器的深层探讨。装饰器的本质是函数,那我们如果不用@的装饰方式,看看怎么样实现同样的功能。
#间接装饰 def printname(func): #装饰器:实质是一个函数,参数是需要装饰的函数名(非函数调用) def wrapper(*args, **kwargs): #可变参数*args和关键字参数**kwargs print(f"[DEBUG]: enter {func.__name__}()") return func(*args, **kwargs) return wrapper #装饰器函数返回的是装饰完的函数 def say_hello(): print("hello!") #return 0 测试line:23 def say_goodbye(): print("goodbye!") if __name__ == '__main__': decorator = printname(say_hello) ''' 不能使用decoratot = printname(say_hello()); 可能是因为调用函数的名称是用func.__name__,而非func().__name__ 用了func().name会报错'NoneType'对象没有'__name__'属性。 因为say_hello()函数没有return,故执行结果为NoneType无。 ''' decorator() ''' 接上绿色注释: >>> type(say_hello()) hello! <class 'NoneType'> >>> type(say_hello) <class 'function'> ''' #func,函数不带括号时,调用的是这个函数本身。是一整个函数体,不须等函数执行完成。 #func(),函数带括号时,调用的是函数的执行结果,需要等待函数执行完成的结果。
和@方法的区别主要在于如何使用装饰器函数。我们需要在调用时把执行函数传入给装饰函数,return 的结果返回给新函数;再运行新函数,就实行了这一效果。
- 最后对装饰函数的代码做一个解读。(应该在前面解读的)
- printname(func)是装饰函数,它的参数是func(一个函数,即执行函数),它的return是wrapper函数。
- 在printname里定义一个wrapper函数(参数、关键字可变),把装饰器的内容包装在wrapper里。wrapper返回的是func(执行函数)。
-
相当于是这一段代码执行了wrapper函数
1 def printname(func): 2 def wrapper(*args, **kwargs): 3 print(f"[DEBUG]: enter {func.__name__}()") 4 return func(*args, **kwargs) 5 return wrapper
实现了:print(装饰)+ func(执行函数),即成功装饰。
- 还有一个很有趣的例子,在https://www.cnblogs.com/cicaday/p/python-decorator.html#4027718046,感谢作者的点拨。
- 装饰器总结:
实质: 是一个函数
参数:是你要装饰的函数名(并非函数调用)
返回:是装饰完的函数名(也非函数调用)
作用:为已经存在的对象添加额外的功能
特点:不需要对对象做任何的代码上的变动
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式