上一篇介绍了装饰器的常规使用方法,即函数形式的装饰器。这篇文章中,进一步介绍类class跟装饰器的相关知识。

【用装饰器来装饰类函数】

  这是今天介绍的第一种使用场景,比较常见。因为目前好多的编程语言都面向对象,于是日常工作中也就难免需要我们去“装饰”它们的方法了。

  先看代码示例:

import time

def debug(func):    #传参接受类的方法
def func1(my,m,n): #原类方法有三个参数,故内层函数需要传递相同的参数
print("a=%d\tb=%d" % (m,n))
func(my,m,n)
return func1 #返回内层函数 class test():
@debug
def myAdd(self,a,b):
print(a + b) if __name__ == "__main__":
myIns=test()
myIns.myAdd(2,3)

  其执行结果如下:

  可见,跟装饰普通的函数的用法一样,只是注意因为装饰的对象是类函数,所以需要跟类函数一样多传递一个表示对象本身的参数即可。

【用类的实例对象来装饰函数】

  这是今天介绍的第二种使用场景,也是比较晦涩的一种用法。因为通过__call__重写运算符()后,类的实例对象也就具有了callable,从而实现跟函数一样形式的使用。

  讲解原理之前,我们先来看一个代码示例(示例中的装饰器需要传参):

import time

class debug(object):
def __init__(self,n): #初始化函数中传递装饰器的参数
self.n=n def __call__(self,func): #实例的调用函数中传递目标函数名称,相当于函数类型装饰器的外层函数
def func1(*args,**kwargs): #内层函数传递目标函数执行所需要的参数
for i in range(1,self.n+1,1):
time.sleep(1)
print("Time flies: %d" % (i))
print("\n\rStarting...")
func(*args,**kwargs) #目标函数真正执行,上下文即为增加的装饰功能
print("Stopped...")
return func1 #return返回内层函数的名称 @debug(3) #装饰器携带参数,让sleep N秒后再执行目标函数
def outputName(name):
print("\tHello,%s" % (name)) if __name__ == "__main__":
outputName("Elon Musk")

  其执行的结果如下:

  大家可能有疑问,为什么装饰器的参数一定要通过初始化函数传递,不能类似函数形式那样直接定义一个三层嵌套的闭包函数吗?答案是:不能!

  下面我们来通过调试代码来看看深藏其中的原理背景。

  首先,我删除初始化函数的定义同时改写__call__()函数使其回归三层嵌套定义的闭包函数形式。此时的执行结果如下:

  为什么会报错呢?因为我们在类的定义中没有明确定义__init__()这个初始化函数,则在类实例化的时候就认为该类不需要参数。但我们的装饰器调用格式却带了参数,因此报错。

  我们都是知错能改的好公民嘛。于是追加初始化函数__init__()的定义,代码改写如下:

import time

class debug(object):
def __init__(self,n):
self.n=n def __call__(self):
def func1(func):
def func2(*args,**kwargs):
for i in range(1,self.n+1,1):
time.sleep(1)
print("Time flies: %d" % (i))
print("\n\rStarting...")
func(*args,**kwargs)
print("Stopped...")
return func2
return func1 @debug(3) #装饰器携带参数,让sleep N秒后再执行目标函数
def outputName(name):
print("\tHello,%s" % (name)) if __name__ == "__main__":
outputName("Elon Musk")

  其执行的结果如下:

  为什么又报错了呢?细看错误提示,是说__call__()只需要一个参数,我们却传递了两个。

  通过上一篇的介绍,我们知道装饰器@的本质相当于把目标函数的名称作为参数传递给了装饰器。所以,debug(3)其实得到的是一个对象实例,此时是要把目标函数名称传参给类的对象。虽然我们通过定义__call__()使得对象具有callable,但__call__()定义的时候只有一个self参数,并没有函数名称这个参数。

  好了,一切真相大白。我们继续改写代码并运行,于是我们最终得到了刚开始看到的那段代码:      

  好了,至此大家应该也都终于明白了为什么通过类来装饰函数的时候一定要通过__call__()来实现,并且装饰器的传参不能通过三层嵌套__call__()来实现。

  下面再补充一个不需要传参的类装饰器代码及其执行结果,比上面的示例简单了许多,个中的道理跟上面类似,大家可以自行体会下(如果有不明白的地方,欢迎大家评论留言,鄙人定当及时回复):

Python装饰器(2)的更多相关文章

  1. 关于python装饰器

    关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...

  2. python装饰器通俗易懂的解释!

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...

  3. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  4. python 装饰器修改调整函数参数

    简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...

  5. python 装饰器学习(decorator)

    最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...

  6. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  7. 关于python装饰器(Decorators)最底层理解的一句话

    一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...

  8. Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...

  9. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

  10. python装饰器方法

    前几天向几位新同事介绍项目,被问起了@login_required的实现,我说这是django框架提供的装饰器方法,验证用户是否登录,只要这样用就行了,因为自己不熟,并没有做过多解释. 今天查看dja ...

随机推荐

  1. 一篇文章图文并茂地带你轻松实践 HTML5 history api

    HTML5 history api 前言 由于笔者在网络上没有找到比较好的关于 history api 的实践案例,有的案例过于杂乱,没有重点,有些案例只是告诉读者 api 是什么,却没告诉怎么用,本 ...

  2. codeforces 5C

    C. Longest Regular Bracket Sequence time limit per test 2 seconds memory limit per test 256 megabyte ...

  3. /usr/lib/nvidia-384/libEGL.so.1 is not a symbolic link

    记得要将384改为自己系统对应的a. sudo mv /usr/lib/nvidia-384/libEGL.so.1 /usr/lib/nvidia-384/libEGL.so.1.org sudo ...

  4. CN_Week2_Neuron_code

    CN_Week1_Neuron_code on Coursera Abstract for week2: -- 1. Technique for recording from the brain. - ...

  5. CDN 工作原理剖析

    CDN 工作原理剖析 CDN / Content Delivery Network / 内容分发网络 https://www.cloudflare.com/zh-cn/learning/cdn/wha ...

  6. convert URL Query String to Object All In One

    convert URL Query String to Object All In One URL / query string / paramas query string to object le ...

  7. Vue 3 In Action

    Vue 3 In Action $ yarn add vue https://v3.vuejs.org demos refs https://v3.vuejs.org/guide/migration/ ...

  8. Flutter 1.17.x

    Flutter 1.17.x Flutter (Channel stable, v1.17.3, on Mac OS X 10.15.5 19F101, locale en-CN) https://f ...

  9. linux bash which

    linux bash which https://linuxize.com/post/linux-which-command/ Linux which command is used to ident ...

  10. js currying & js 科里化

    js currying & js 科里化 var test = ( function (a){ console.log(`a2 =`, a);// 1 // console.log(`b2 = ...