Python 装饰器 续集

前言:

Python 装饰器 一节中,我们引入了装饰器的概念,以及常见的4中装饰器模型,本节我们会探索一下装饰器的装饰过程,以及装饰器装饰类的情况,以及装饰器使用的注意事项。

装饰器的装饰过程: 装饰时部分代码会执行

先来一个简单的示例:

def log(func):
    print('start to decorate -----------')
    def wrapper():
        print('log---------------start')
        func()
        print('log---------------end')
    return wrapper

@log
def hh():
    print('hhhhhhhh')
    
print('================')
hh()
输出:
start to decorate -----------
================
log---------------start
hhhhhhhh
log---------------end

如码:使用·log()来装饰 hh() 从输出我们可以看到,在print('======')之前还会有一段输出,是在装饰器函数log()中的一段代码,可见在@log装饰def hh()时是会有部分代码执行的。然后当执行hh()时会有其他代码执行。解释一下过程:

  1. @loghh = log(hh) 代码从上到下执行 print('start to decorate ------')则控制台输出相应信息
  2. 然后 def wrapper(): python解释器到这直接跳过, 然后到return wrapper 所以此时,hh = wrapper
  3. hh()wrapper() 然后执行wrapper() 中的代码

再来看一下带参装饰器的版本即hh() = log(info)(hh)()

def log(info):
    print('------%s------'% info)
    def decorator(func):
        print('start to decorate -----------')
        def wrapper():
            print('log---------------start')
            func()
            print('log---------------end')
        return wrapper
    return decorator

@log('im info')
def hh():
    print('hhhhhhhh')
    
print('================')
hh()
输出:
------im info------
start to decorate -----------
================
log---------------start
hhhhhhhh
log---------------end

为什么会在=======前输出俩行信息呢? 按照上面的思路是因为@log('i'm info') 时 装饰过程执行到了 wrapper() 然后返回了函数。利用我们Python 装饰器 一节中给出的公式 @3.带参装饰器装饰无参函数: hh() = log(text)(hh)()确实是装饰到return wrapper.因此会有如上的输出。

嵌套装饰:先装饰后执行

更进一步理解装饰器的执行过程,上代码:

def log_a(func):
    print('---im log_a  decorator----------')
    def wrapper():
        print('----------start----log_a')
        func()
        print('----------end------log_a')
    return wrapper

def log_b(func):
    print('---im log_b  decorator----------')
    def wrapper():
        print('----------start----log_b')
        func()
        print('----------end------log_b')
    return wrapper

@log_b
@log_a
def hh():
    print('hhhhhhhh')
    
print('================')
hh()
输出:
---im log_a  decorator----------
---im log_b  decorator----------
================
----------start----log_b
----------start----log_a
hhhhhhhh
----------end------log_a
----------end------log_b

怎么样是不出乎你的意料了,为了讲的比较清楚此处借助图来理解一下,no picture say a j8 上图:

画的什么鬼~~ 好吧~感觉不是很贴切,看来有待提高,大致解释下:

  1. @log_b @log_a双重装饰,相当于: hh = log_b( log_a(hh) ) 所以装饰的顺序是,先装饰@log_a 后装饰 @log_b 因此在装饰的时候会有俩条信息输出,是先输出def log_a() 然后输出 def log_b()
  2. 装饰的结果,相当于图右边的嵌套模型,即执行 hh()时 从上到下右边的代码块,最外层是log_b() 最内层当然是hh(),所以有上面的输出结果
小结:
  1. python装饰时会执行一部分代码,运行时执行一部分代码
  2. 多重嵌套装饰,先装饰后执行 (log_a 先装饰)

装饰器装饰类:

升麻?装饰器还可以装饰类?没错!装饰器可以装饰类,事实上可以吧类看做特殊的函数,再进一步类也是对象,函数也是对象,在python 中只要这个对象实现了__call__函数就可以被调用,即可以像函数一样fun() 会执行内部的代码,今天不深入讲解关于__call__的机制。
上代码(下面是python实现单例模式的一种方法):

def singleton(cls):
    __instances = {}
    print(cls)
    def wrapper(*args,**kwargs):
        if cls not in __instances:
            __instances[cls] = cls(*args,**kwargs)
        return __instances[cls]
    return wrapper

@singleton
class A(object):
    def hh(self):
        print('hhhhhhhh')

print("========")
for i in range(5):
    i = A()
    print('-------',i)

输出:
<class '__main__.A'>
==========
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>
------- <__main__.A object at 0x000000000B353518>

确实是类也可以被装饰器装饰的,因为类A() 相当于一个函数,A为函数名,没什么好讲的就是可以,上面的代码适合于单线程的单例模型。要学习更多的关于python 单例的实现方式请转 python 单例模式

装饰器的注意事项:functools.wraps

函数也是个对象,所以可以将函数赋值给一个变量。然后直接执行该变量就相当于执行了之前的函数

def hh():
    print('hhhhhhhh')
    
a = hh
a()
print(a.__name__)
输出:
hhhhhhhh
hh

如上,函数有一个__name__ 属性,是函数名。将函数赋值给变量a后,a即指向了hh()函数,此时执行a()就相当于执行了hh()。外推到装饰器中如下:

def log(func):
    def wrapper():
        func()
    return wrapper

@log
def hh():
    print('hhhhhh')

hh()
print(hh.__name__)
输出:
hhhhhh
wrapper

此时print('hh.__name__') 输出的是wrapper。因为此时的hh=wrapper。这可能会在不知道的地方发生一些不知名的错误,所以我们装饰函数后应该吧,该参数给修改回去。可以显示的修改回去即,在return wrapper 之前 wrapper.__name__ = func.__name__当然python不会吧这么low的事甩给我们,python总是那么体贴。可以使用Python内置的functools.wraps()装饰器,来解决该问题,如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper():
        func()
    #wrapper.__name__ = func.__name__
    return wrapper

@log
def hh():
    print('hhhhhh')

hh()
print(hh.__name__)
输出:
hhhhhh
hh

可以看到确实是将参数__name__修改回去了

提前预告:

下一篇:Python 装饰器 应用
主要分析一些装饰器,常用的示例,让我们更加流利的使用装饰器

总结:

  • python装饰时会执行一部分代码,运行时执行一部分代码
  • 多重嵌套装饰,先装饰后执行 (类似栈结构)
  • 多重装饰:hh = log_b( log_a(hh) )
  • 类也可以被装饰,类相当于一个函数可以被调用
  • @functools.wraps(func) 修改被装饰函数的__name__参数

声明:

本人也是python 小白,如果上述内容有讲的不对的地方还请各位批评指点。将不胜感激,再次感谢~~~

参考资料:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 包(lib)、模块(module) 在Python中,存在包和模块两个常见概念。 模块:编写Python代码的py...
    清清子衿木子水心阅读 3,802评论 0 27
  • Python进阶框架 希望大家喜欢,点赞哦首先感谢廖雪峰老师对于该课程的讲解 一、函数式编程 1.1 函数式编程简...
    Gaolex阅读 5,496评论 6 53
  • 这两天感情特别脆弱,不知道是因为大姨妈拜访还是因为看了《一条狗的使命》打开了泪闸,一点点的小情绪都会导致默默地...
    二拾三阅读 247评论 0 0
  • 个人观点,不过这都是我和朋友曾探讨过的问题。当你揣着录取通知书走进大学校园,你便是一名大学生了。不知道你有没有考虑...
    阅读前沿阅读 774评论 0 1
  • 一,准备 听腾哥音频听了n遍,用高倍速阅读发三个小时读完《少有人走的路心智成熟的旅程》虽然理解不一定透彻,...
    破茧成蝶a阅读 300评论 1 3