Python学习 day12
一、@wraps
__name__ 查看函数的名字
__doc__ 查看函数的文档字符串
例:
def func(arg): """ 这是一个测试函数,这里是函数说明 :param arg: 参数说明 :return: 返回值说明 """ return arg print(func.__name__) print(func.__doc__)
结果:
但是,经过装饰器装饰的函数,我们知道虽然调用与原来一致,但原函数名实际指向的却是装饰器中的inner函数,所以在调用__name__和__doc__时,得到的结果,如下:
def wrapper(func): def inner(*args, **kwargs): """ 这是一个装饰器内部函数,对原函数进行装饰 :param args: 原函数中的位置传参 :param kwargs: 原函数中的按关键字传参 :return: 返回原函数执行后的结果 """ print("在原函数之前") ret = func(*args, **kwargs) print("在原函数之后") return ret return inner @wrapper def test(arg): """ 这是一个测试函数,这里是函数说明 :param arg: 参数说明 :return: 返回值说明 """ return arg print(test.__name__) print(test.__doc__)
结果:
可以看到,被装饰的函数调用__name__和__doc__得到的不再是原函数的名称和文档字符串
为了解决这个问题,我们引入wraps装饰inner函数,就可以正常获取元函数的函数名和文档字符串了,如下:
from functools import wraps def wrapper(func): @wraps(func) # 用wraps装饰inner函数 def inner(*args, **kwargs): """ 这是一个装饰器内部函数,对原函数进行装饰 :param args: 原函数中的位置传参 :param kwargs: 原函数中的按关键字传参 :return: 返回原函数执行后的结果 """ print("在原函数之前") ret = func(*args, **kwargs) print("在原函数之后") return ret return inner @wrapper def test(arg): """ 这是一个测试函数,这里是函数说明 :param arg: 参数说明 :return: 返回值说明 """ return arg print(test.__name__) print(test.__doc__)
执行以上代码,得到:
二、带参数的装饰器
在有些情况下,需要使用带参数的装饰器,把外部的变量值传入装饰器中,进行相关操作。
举例:如有一种情况,项目经理需要测试每个人设计的函数的执行时间,以此来对每个人的函数进行评测,这时就需要增加函数执行的计时功能,需要用到装饰器。在评测结束后,项目经理又要求将计时功能去掉,这如果把装饰器再一个个去除就会非常麻烦,因此需要一种变通的方法能保证需要的时候增加装饰器,不需要时去掉。
就这个例子设计一个装饰器,我们有如下方法:
import time FLAG = True # 标志位 def timmer_out(FLAG): def timmer(func): def inner(*args, **kwargs): if FLAG: # FLAG为True时,增加计时功能 start = time.time() ret = func(*args, **kwargs) end = time.time() print(end - start) return ret else: # False时不装饰 return func(*args, **kwargs) return inner return timmer def func1(): print("执行func1") time.sleep(3) print("func1结束") @timmer_out(FLAG) # 语法糖,实际如下面func1 def func2(): print("执行func2") time.sleep(5) print("func2结束") t = timmer_out(FLAG) func1 = t(func1) func1()
func2()
结果:
若FLAG=False,结果:
可以看到,通过控制FLAG的值就可以控制是否给原函数增加装饰,解决了问题。
其实timmer_out函数就做了一个利用闭包传参的功能,若没有timmer_out这个外部函数,里面inner想使用全局变量FLAG的值在python中也是可以的,但这样的用法不是程序设计推荐的,不如由外部函数timmer_out传入更规范。
三、多个装饰器装饰一个函数
多个装饰器装饰一个函数依然类似java中的过滤器和拦截器,与aop思想一致,因多个装饰器都对函数有增加功能,那执行的顺序是什么呢,如:
def wrapper1(func): def inner(*args, **kwargs): print("-------wrapper1在函数调用前--------") ret = func(*args, **kwargs) print("-------wrapper1在函数调用后--------") return ret return inner def wrapper2(func): def inner(*args, **kwargs): print("-------wrapper2在函数调用前--------") ret = func(*args, **kwargs) print("-------wrapper2在函数调用后--------") return ret return inner def wrapper3(func): def inner(*args, **kwargs): print("-------wrapper3在函数调用前--------") ret = func(*args, **kwargs) print("-------wrapper3在函数调用后--------") return ret return inner @wrapper2 @wrapper1 @wrapper3 def func(): print("我是被装饰函数") func()
结果:
可看到,装饰器的执行顺序是根据装饰顺序来的:2-1-3-func-3-1-2
四、零散知识点
1、函数的注释
在python中写的函数,也有函数注释,类似java的方法注释,用于说明函数的主要功能,参数和返回值。
具体写法:
def func(arg): """ 这是一个测试函数,这里是函数说明 :param arg: 参数说明 :return: 返回值说明 """ return arg
在pycharm中函数下面直接输入三个”工具会自动显示函数注释的内容
2、os.path.getsize()
从os模块引入getsize()函数,用于判断文件大小。例:
import os print(os.path.getsize('test')) # 获取test文件的大小,单位byte
五、其他
课程老师推荐了两本书《Python核心编程》、《流畅的python》,可以买来看看。