美文网首页
Python 装饰器 续集

Python 装饰器 续集

作者: snailpy | 来源:发表于2018-08-22 19:28 被阅读0次

前言:

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 小白,如果上述内容有讲的不对的地方还请各位批评指点。将不胜感激,再次感谢~~~

参考资料:

相关文章

  • Python 装饰器 续集

    前言: Python 装饰器 一节中,我们引入了装饰器的概念,以及常见的4中装饰器模型,本节我们会探索一下装饰器的...

  • 装饰器模式

    介绍 在python装饰器学习 这篇文章中,介绍了python 中的装饰器,python内置了对装饰器的支持。面向...

  • python中的装饰器

    python装饰器详解 Python装饰器学习(九步入门) 装饰器(decorator) 就是一个包装机(wrap...

  • [译] Python装饰器Part II:装饰器参数

    这是Python装饰器讲解的第二部分,上一篇:Python装饰器Part I:装饰器简介 回顾:不带参数的装饰器 ...

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • Python进阶——面向对象

    1. Python中的@property   @property是python自带的装饰器,装饰器(decorat...

  • Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方

    Python 装饰器简介装饰器(Decorator)是 Python 非常实用的一个语法糖功能。装饰器本质是一种返...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • python3基础---详解装饰器

    1、装饰器原理 2、装饰器语法 3、装饰器执行的时间 装饰器在Python解释器执行的时候,就会进行自动装饰,并不...

  • 2019-05-26python装饰器到底是什么?

    装饰器例子 参考语法 装饰器是什么?个人理解,装饰器,是python中一种写法的定义。他仍然符合python的基本...

网友评论

      本文标题:Python 装饰器 续集

      本文链接:https://www.haomeiwen.com/subject/udaeiftx.html