美文网首页
Python装饰器

Python装饰器

作者: 什么23 | 来源:发表于2019-11-13 01:49 被阅读0次
# 这是一个装饰器的简单例子

@dec
def func():
    pass

什么是装饰器

装饰器可以在不改变原有对象的代码及调用方式的情况下,为原有的对象增加新的功能或限制条件。装饰器有函数装饰器,也有类装饰器。装饰器体现的是开放封闭原则,即对功能扩展的开放,对修改已实现功能的封闭。

装饰器本质上就是Python的函数,它和普通的函数没有任何区别,只是有了特殊的用法,所以在特殊用法下就有了装饰器这个名字。

装饰器怎么用

现在有一个hello函数,功能很简单,只是打印hello world!

def hello():
    print('hello world!')
    
hello()

它的执行结果为:

hello world!

现在要对这个函数进行一下功能的扩展。

def dec(func): # 这个就是装饰器,参数就是被装饰的函数
    def wrapper():
        print('start...')
        func()
        print('end...')
    return wrapper
    
@dec # 这是装饰器的用法
def hello(): 
    print('hello world!')
    
hello() #函数的调用方法和上边一样没有改变    

现在的执行结果为:

start...
hello world!
end...

如上面的代码显示,函数dec就是装饰器,函数hello就是被装饰的函数。而装饰器的用法,就是hello函数上面的@dec。注意,这里应用装饰器只是写了dec这个函数名,而不是dec()这样调用它。

装饰器的运行机制

还是上面的例子。

  1. 解释器读到函数dec,也就是装饰器。这时它还没有被调用,所以只是被存在了内存中,并没有被执行。
  2. 到了@dec。@dec和dec()一样,都会执行这个函数,但是不同的是,@dec会把被装饰的hello函数的本身(而不是函数执行结果)当作参数,传入到dec函数中,相当于执行了dec(hello)这个函数。
  3. 这时开始执行dec函数。首先会把wrapper函数存入到内存中(并不是执行),然后返回wrapper函数本身(也不是执行)给hello函数。也就是,hello函数被装饰完之后,内存地址被指向到了wrapper函数的内存地址。
  4. 执行hello函数。由于hello函数的地址已经被指向到了wrapper函数,所以执行结果就是wrapper函数的执行结果。wrapper函数中的func这个函数地址才是原来hello函数的内存地址。所以使用装饰器之后,表面上不会改变原来函数调用的方法。

以上就是装饰器的运行机制。

为什么是嵌套函数

刚开始学习装饰器的人可能会比较疑惑,装饰器为什么要用嵌套函数?感觉好多功能不用嵌套也可以实现。下面看例子。

def dec(func):    
    print('start...')    
    func()    
    print('end...')

@dec
def hello():    
    print('hello world!')

执行结果为:

start...
hello world!
end...

这个执行结果看起来没有问题。是的,执行结果是没有问题,但是问题是还没有写执行语句却已经有了执行结果。

造成这个问题的原因就在@dec这里。@dec就是执行装饰器的语句,所以它就执行了dec这个函数。这就是为什么,还没有写执行语句却有了执行结果的原因。这也是为什么装饰器要用嵌套函数的原因,再用一个函数封装一下,避免在定义阶段就给执行了。

被装饰的函数带参数

def hello(string):
    print(f'hello {string}!')
    
    
hello('python')

执行结果为:

hello python!

这是一个带参数的函数,那么这种函数如何添加装饰器呢?

其实,只要理解了被装饰的函数会被指向到装饰器里的函数,那么在装饰器里的函数加上参数就解决了传参的问题。下面看例子。

def dec(func):
    def wrapper(string): # 添加和hello一样的参数即可
        print('start...')
        func(string) # 通过wrapper函数传递来的参数
        print('end...')
    return wrapper

@dec
def hello(string): # 内存地址会被指向wrapper的内存地址
    print(f'hello {string}!')

hello('python')

执行的结果为:

hello python!

装饰器带参数

如果理解了前边的内容,那么理解装饰器带参数也就比较容易了。下面先看例子。

def outer(name): # 多了一层函数,用于接收传递装饰器的参数
    def dec(func):
        def wrapper(string):
            print(f'{name} start...')
            func(string)
            print(f'{name} end...')
        return wrapper
    return dec

@outer('haha')  # 装饰器执行语句多了括号和参数
def hello(string):
    print(f'hello {string}!')

hello('python')

执行结果为:

haha start...
hello python!
haha end...

首先可以看到,装饰器在定义的时候又多了一层嵌套,这层嵌套用于接收传递参数,并且返回下一层的嵌套函数。

然后可以看到装饰器多了括号和参数。现在说的就是带参数的装饰器,所以带参数没有什么稀奇的。

最主要的是多的那个括号。装饰器执行语句多了括号之后,它的意思也稍微有了变化。我们知道,执行函数就是函数名加上括号。那么 @outer('haha') 的执行顺序是下面这样的。

  1. 先执行outer('haha')这个函数,这时它的参数是'haha'。
  2. outer函数返回值是dec,所以@outer('haha') = @dec,并把参数传到了函数内。
  3. @dec这个语句就是我们前边熟悉的装饰器的执行语句了。它会把hello函数本身当作参数传递给函数dec,并把内存地址指向到了wrapper函数。后边执行过程就跟前边的例子一样,也就不再赘述了。

主要知道@outer()会先执行outer函数,而不是把hello当作参数的装饰器语句,那么对于带参数的装饰器也就没什么难点了。

叠加装饰器

装饰器也可以叠加使用,还是先看例子。

def dec1(func):
    print('这是第一层')
    def wrapper():
        print('这是第1层开始')
        func()
        print('这是第1层结束')
    return wrapper

def dec2(func):
    print('这是第二层')
    def wrapper():
        print('这是第2层开始')
        func()
        print('这是第2层结束')
    return wrapper

@dec2
@dec1
def hello():
    print('hello world!')

print('开始')
hello()

执行结果为:

这是第一层
这是第二层
开始
这是第2层开始
这是第1层开始
hello world!
这是第1层结束
这是第2层结束

这个结果可能刚开始看有些不那么好理解,我们慢慢看。

先看执行结果的前三行。这三行内容里,首先执行的是两个装饰器的内容,之后才是print语句。从这个执行结果可以看出,python是从上到下顺序执行,并在遇到装饰器执行语句的时候会自动执行装饰器函数('第几层开始'没有跟着执行是因为被封装进了函数,要执行语句才能执行)。再从首先执行了第一层又执行了第二层可以看出,遇到叠加装饰器时是从下向上执行的。

明白了装饰器是从下向上执行,那么就是下一层装饰器的函数就是它上一层装饰器的参数。

@dec2  # 相当与dec2(dec1(hello))
@dec1  # 相当于dec1(hello)
def hello():

这么看这个执行结果就好理解了。

结束

这篇文章主要是帮助初步理解装饰器和一些简单的用法,在这里就不详细说明其他更深奥的用法了(主要是我不会),那么这篇文章也就到这里了。

相关文章

  • 装饰器模式

    介绍 在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的基本...

  • 2018-07-18

    Python装饰器 装饰,顾名思义,是用来打扮什么东西的。Python装饰...

网友评论

      本文标题:Python装饰器

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