博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python 装饰器
阅读量:6601 次
发布时间:2019-06-24

本文共 3964 字,大约阅读时间需要 13 分钟。

1. 什么是装饰器?

  顾名思义,装饰器就是在方法上方标一个带有@符号的方法名,以此来对被装饰的方法进行点缀改造。

  当你明白什么是装饰器之后,自然会觉得这个名字取得恰如其分,但作为初学者来说多少还是会有些迷茫。下面用代码来说明怎么理解装饰器。

#脚本1def target():    print(this is target)    def decorator(func):    func()    print(this is decorator)    decorator(target)-------------------------------------------    运行结果为:    this is targetthis is decorator

Python允许将方法当作参数传递,因此以上脚本就是将target方法作为参数传入decorator方法中,这其实也是装饰器的工作原理,以上代码等同于:

#脚本2def decorator(func):    func()    print(this is decorator)    @decoratordef target():    print(this is target)    target-------------------------------------------运行结果:    this is targetthis is decorator

因此可以看出,所谓的装饰器就是利用了Python的方法可作参数传递的特性,将方法target作为参数传递到方法decorator中。

@decorator

def target():
   ...

  这种在一个方法的上方加一个@符号的写法,就是表示位于下方的方法将被作为参数传递到位于@后面的decorator方法中。使用@符号只是让脚本1中的代码换了一个写法,更加好看,当然也会更加灵活与实用,后面会讲到这点。但它们的本质其实是一样的,这也就是装饰器的工作原理。

  2. 装饰器的原理

  如果你仔细看的话,会在脚本2中发现一个问题,那就是脚本2中最后一行的target只是一个方法名字,它不是正确的方法调用,正确写法应该加上左右括号的target(),如下:

#脚本3    def decorator(func):    func()    print(this is decorator)    @decoratordef target():    print(this is target)    target()--------------------------------------------运行结果:    this is targetthis is decoratorTraceback (most recent call last):  File "C:/Users/Me/Desktop/ff.py", line 34, in 
target()TypeError: NoneType object is not callable

正如你所看到的,如果按照正确的写法,运行结果你会看到应该出现的两行打印文字"this is target"和"this is decorator",还会出现错误提示,ff.py是我为写这篇心得临时编写的一个py脚本名字,提示说'NoneType'对象不可调用。这是怎么回事?好吧,我现在必须告诉你,其实脚本2和脚本3中并不是一个使用装饰器的正确写法,不是使用错误,而是作为装饰器的decorator方法写的并不友好,是的,我不认为它是错误的写法,只是不友好。但只要你明白其中的道理,使用恰当的手段也是可以运行正常的,这就是为什么脚本2看似写错了调用方法却得出了正确的结果。当然学习还是得规规矩矩,后面我会具体说正确的装饰器怎么书写,在这里我先解释了一下脚本2和脚本3的运行原理,了解它们的运行原理和错误原因,其实就是了解装饰器的原理。

  脚本2和脚本3的区别在于target和target(),也就是说真正的差别在于()这个括号。当()被附加在方法或者类后面时,表示调用,或者称为运行及实例化,无论称呼怎样,本质意义没有不同,都是调用给出的对象,当对象不具备调用性的时候,就会报错:'某个类型' object is not callable。当一个方法被调用后,即target(),是否能被再次执行,取决于它是否会return一个对象,并且该对象可以被调用。也许你会有点迷糊,对比一下代码会比较容易理解我想表达的意思:

>>>def returnme():>>>    print(this is returnme)     >>>def target():>>>    print(this is target)      >>>target
>>>target()target
>>>target()()targetreturnme >>>returnme()()returnmeTraceback (most recent call last): File "
", line 1, in
returnme()()TypeError: NoneType object is not callable

如上所示,当直接在脚本中输入target,它只是告诉编译器(我想是编译器吧,因为我也不是很懂所谓编译器的部分),总之就是告诉那个不知道在哪个角落控制着所有python代码运行的“大脑”,在

0x00000000030A40D0位置(这个位置应该是指位置)存有一个function(方法)叫target;在target后面加上(),表示调用该方法,即输入target(),“大脑”便按照target方法所写的代码逐条执行,于是打印出了target字符串,并且“大脑”明白在0x00000000030A4268位置有一个叫returnme的方法;因为target对象调用后是会返回一个returnme方法,并且方法是可以被调用的,因此你可以直接这样书写target()(),“大脑”会逐条执行target中的代码,然后return一个returnme,因为多加了一个(),表示要对返回的returnme进行调用,于是再次逐条执行returnme中的代码,最后便能看到15、16的打印结果;而returnme方法是没有返回任何可调用的对象,因此当输入returnme()()时,“大脑”会报错。

  下面我们可以来解释一下脚本2和脚本3的运行详情,之前说过,装饰器的工作原理就是脚本1代码所演示的那样。

@decoratordef target():    ...    等同于    def decorator(target)():    ...    注:python语法中以上写法是非法的,以上只是为了便于理解。

当你调用被装饰方法target时,其实首先被执行的是作为装饰器的decorator函数,然后“大脑”会把target方法作为参数传进去,于是:

#脚本2def decorator(func):    func()    print(this is decorator)      @decoratordef target():    print(this is target)      target-------------------------------------------    实际运行情况:首先调用decorator方法:decorator()因为decorator方法含1个参数,因此将target传入:decorator(target)运行代码“func()”,根据传入的参数,实际执行target(),结果打印出:this is target运行代码"print(this is decorator)",结果打印出:this is decorator

对比脚本3的运行情况:

#脚本3def decorator(func):    func()    print(this is decorator)      @decoratordef target():    print(this is target)      target()-------------------------------------------    实际运行情况:首先调用decorator方法:decorator()因为decorator方法含1个参数,因此将target传入:decorator(target)运行代码“func()”,根据传入的参数,实际执行target(),结果打印出:this is target运行代码"print(this is decorator)",结果打印出:this is decorator    以上与脚本2中运行情况完全相同,接下来便是执行脚本2中target没有的(),也就是执行调用命令。由于decorator(target)没有返回一个可以被调用的对象,因此“大脑”提示错误:NoneType object is not callable

转载于:https://www.cnblogs.com/wumac/p/6514084.html

你可能感兴趣的文章
C++中public、protected及private用法
查看>>
苹果公司的产品已用完后门与微软垄断,要检查起来,打架!
查看>>
chrome调试ajax
查看>>
centos 升级php、mysql(webtatic)
查看>>
Java并发编程:Lock
查看>>
oracle服务器和客户端字符集的查看和修改
查看>>
顶级的JavaScript框架、库、工具及其使用
查看>>
AYUI -AYUI风格的 超美 百度网盘8.0
查看>>
linux下php中文UTF-8转换Unicode方法和注意事项
查看>>
TensorFlow:tf.contrib.layers.xavier_initializer
查看>>
简明 Python 教程
查看>>
Photoshop操作指南
查看>>
用MPMoviePlayerController做在线音乐播放
查看>>
ASP.NET调用cmd命令提示符拒绝访问解决方案
查看>>
Leetcode: Construct Binary Tree from Preorder and Inorder Transversal
查看>>
嵌入式开发之字符叠加---gb2313 国标码,utf8 国际码,unicode 无码
查看>>
Java查找算法——二分查找
查看>>
如何构建微服务架构
查看>>
【前端笔记】彻底理解变量与函数的声明提升
查看>>
PHP工具箱:PHPStan —— PHP 静态代码分析工具
查看>>