输出结果为:
这个现象的原因是,装饰行为本身 , 是通过构造了一个新的函数(例子中是wrap_func函数)来实现装饰这个行为的 , 然后把这个修改后的函数赋给了原函数名 。
这样,会导致我们预期的被装饰函数的一些系统变量(比如__name__)发生了变化 。
对此,python提供了解决方案:
经过这个行为后,被装饰函数的系统变量问题被解决了
输出结果为
刚才的例子都比较简单,被装饰的函数是没有参数的 。如果被装饰的函数有参数,只需要在定义装饰行为时(事实上,这个才更通用) , 增加(*args, **kwargs)描述即可
之前的描述中可以感受到,对于例子中的装饰行为(前后加打印),函数被装饰后,本质上是调用了新的装饰函数wrap_func 。
因此,如果原函数需要有输入参数传递,只需要在wrap_func(或其他任意名字的装饰函数)定义时,也增加参数输入(*args, **kwargs),并将这些参数,原封不动地传给待装饰函数f 。
这种定义装饰行为的方式更具有普遍性,忘记之前的定义方式吧
我们试一下
输出
这里需要注意的是,如果按照以下的方式定义装饰器
那么以下语句将不会执行
因为装饰后实际的函数wrap_func(虽然名字被改成了原函数,系统参数也改成了原函数),运行到return f(*args, **kwargs) 的时候已经结束了
因为装饰器my_decorator本身也是可以输入的,因此 , 只需要在定义装饰器时,增加参数,并在后续函数中使用就可以了 , 比如
此时装饰器已经可以有输入参数了
输出
你可能发现,为什么不用简写版的方法了
因为以上代码会报错?。?
究其原因,虽然
等价于
但是,
并不等价于
这本身和@语法有关,使用@my_decorator时,是系统在应用一个以单个函数作为参数的闭包函数 。即 , @是不能带参数的 。
但是你应该发现了 , 之前的@wraps(f)不是带参数了吗?请仔细观察以下代码
通过一层嵌套 , my_decorator_with_parma本质上是返回了一个参数仅为一个函数的函数(my_decorator) , 但因为my_decorator对my_decorator_with_parma来说是一个闭包,my_decorator_with_parma是可以带参数的 。(这句话真绕)
通过以上的定义,我们再来看
可以这么理解,my_decorator_with_parma(msg='yusheng')的结果是原来的my_decorator函数,同时,因为my_decorator_with_parma可以传参,参数实际上是参与了my_decorator的(因为my_decorator对my_decorator_with_parma是闭包) , my_decorator_with_parma(msg='yusheng')全等于一个有参数参加的my_decorator
因此,以上代码等价于有参数msg传递的
比较绕,需要理解一下 , 或者干脆强记这种范式:
以上范式包含函数的输入输出、装饰器的输入,可以应对大部分情况了 。
实验一下:
输出
以上是一个log装饰器,利用datetime统计了函数的耗时,
并且,装饰器可以进行输出文件操作 , 如果给出了文件路径,则输出文件,否则就打印 。
利用这个装饰器,可以灵活地进行耗时统计
不设置输出文件地址,则打印 。运行结果为:
也可以输出到文件
输出结果为
同时在当前目录生成了一个test.log 文件,内容为:
以上的装饰器都是以函数形式出现的,但我们可以稍做改写,将装饰器以类的形式实现 。
这个装饰器类Log 上个例子里的装饰器函数log功能是一样的,同时,这个装饰器类还可以作为基类被其他继承,进一步增加功能 。
推荐阅读
- word图表如何设置总数,word图表如何设置总数显示
- 猎场拍摄花絮是什么意思,猎场片花
- 北京耐化学作用gis保温罩的简单介绍
- 斗鱼直播calpis,斗鱼直播电视版怎么没了
- go语言在哪里培训 go语音培训
- 直通车如何推广商品,直通车的推广流程
- 直播悬浮窗有什么作用,直播悬浮窗有什么作用吗
- 电脑连不了路由器怎么设置,电脑连不上路由器设置
- mysql5怎么建数据库 mysql数据库怎么创建数据表