以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读你之前的这个的代码,稍微搞清楚一点它的逻辑,才敢给它添加新的东西。这样是不是很繁琐,要是你之前写的代码足够乱足够长,再去读它是不是很抓狂...。实际工作中,我们常常会遇到这样的场景,可能你的需求还不只是这么简单。那么有没有一种可以不对源码做任何修改,并且可以很好的实现你所有需求的手段呢?答案当然是有,这就是今天我们要介绍的python装饰器。

有了装饰器,你除了不用担心前面提到的问题,并且还可以很好的处理接下来要做的事:那就是现在你又有了一个新的需求,比如为另一个函数添加计时功能,这时就非常简单了,把要装饰的函数丢给装饰器就好了,它会自动给你添加完功能并返回给你。是不是很神奇?下面我们将一层层剥开它的神秘面纱。

1. 闭包函数

  在看装饰器之前,我们先来搞清楚什么是闭包函数。python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们就可以理解在函数内创建一个函数的行为是完全合法的。这种函数被叫做内嵌函数,这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错,例如:

而如果内部函数里引用了外部函数里定义的对象(甚至是外层之外,但不是全局变量),那么此时内部函数就被称为闭包函数。闭包函数所引用的外部定义的变量被叫做自由变量。闭包从语法上看非常简单,但是却有强大的作用。闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起。下面给出一个简单的闭包的例子:

def count():
    a = 1
    b = 1
    def sum():
        c = 1
        return a + c  # a - 自由变量
    return sum

总结:什么函数可以被称为闭包函数呢?主要是满足两点:函数内部定义的函数;引用了外部变量但非全局变量。

2. python装饰器

  有了闭包函数的概念,我们再去理解装饰器会相对容易一些。python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。从上面这段描述中我们需要记住装饰器的几点属性,以便后面能更好的理解:

    实质: 是一个函数

    参数:是你要装饰的函数名(并非函数调用

    返回:是装饰完的函数名(也非函数调用

    作用:为已经存在的对象添加额外的功能

    特点:不需要对对象做任何的代码上的变动

python装饰器有很多经典的应用场景,比如:插入日志、性能测试、事务处理、权限校验等。装饰器是解决这类问题的绝佳设计。并且从引入中的列子中我们也可以归纳出:装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑

2.1  函数装饰器

函数的函数装饰器

我们还是以为函数添加计时功能为例,讲述函数装饰器。

import time

def decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func()
        end_time = time.time()
        print(end_time - start_time)

    return wrapper

@decorator
def func():
    time.sleep(0.8)

func() # 函数调用
# 输出:0.800644397735595

在上面代码中 func是我要装饰器的函数,我想用装饰器显示func函数运行的时间。@decorator这个语法相当于 执行 func = decorator(func),为func函数装饰并返回。在来看一下我们的装饰器函数 - decorator,该函数的传入参数是func (被装饰函数),返回参数是内层函数。这里的内层函数-wrapper,其实就相当于闭包函数,它起到装饰给定函数的作用,wrapper参数为*args, **kwargs。*args表示的参数以列表的形式传入;**kwargs表示的参数以字典的形式传入:

从图中我们可以看到:凡是以key=value形式的参数均存在kwargs中,剩下的所有参数都以列表的形式存于args中。这里要注意的是:为了不破坏原函数的逻辑,我们要保证内层函数wrapper和被装饰函数func的传入参数和返回值类型必须保持一致。

类方法的函数装饰器

  类方法的函数装饰器和函数的函数装饰器类似。

import time

def decorator(func):
    def wrapper(me_instance):
        start_time = time.time()
        func(me_instance)
        end_time = time.time()
        print(end_time - start_time)
    return wrapper

class Method(object):

    @decorator
    def func(self):
        time.sleep(0.8)

p1 = Method()
p1.func() # 函数调用

对于类方法来说,都会有一个默认的参数self,它实际表示的是类的一个实例,所以在装饰器的内部函数wrapper也要传入一个参数 - me_instance就表示将类的实例p1传给wrapper,其他的用法都和函数装饰器相同。

2.2 类装饰器

  前面我们提到的都是让 函数作为装饰器去装饰其他的函数或者方法,那么可不可以让 一个类发挥装饰器的作用呢?答案肯定是可以的,一切皆对象嚒,函数和类本质没有什么不一样。类的装饰器是什么样子的呢?

class Decorator(object):
    def __init__(self, f):
        self.f = f
    def __call__(self):
        print("decorator start")
        self.f()
        print("decorator end")

@Decorator
def func():
    print("func")

func()

这里有注意的是:__call__()是一个特殊方法,它可将一个类实例变成一个可调用对象:

p = Decorator(func) # p是类Decorator的一个实例
p() # 实现了__call__()方法后,p可以被调用

要使用类装饰器必须实现类中的__call__()方法,就相当于将实例变成了一个方法。

2.3  装饰器链

  一个python函数也可以被多个装饰器修饰,要是有多个装饰器时,这些装饰器的执行顺序是怎么样的呢?

可见,多个装饰器的执行顺序:是从近到远依次执行。

2.4  python装饰器库 - functools

 
def decorator(func):
    def inner_function():
        pass
    return inner_function

@decorator
def func():
    pass

print(func.__name__)

# 输出: inner_function
 

上述代码最后执行的结果不是 func,而是 inner_function!这表示被装饰函数自身的信息丢失了!怎么才能避免这种问题的发生呢?

可以借助functools.wraps()函数:

 
from functools import wraps
def decorator(func):
    @wraps(func)
    def inner_function():
        pass
    return inner_function

@decorator
def func():
    pass

print(func.__name__)

#输出: func

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

  1. 我终于弄懂了Python的装饰器(二)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我终于弄懂了Python的装饰器(三) 4. 我终于弄懂了Python的装饰器(四) 二 ...

  2. python函数知识七 闭包、装饰器一(入门)、装饰器二(进阶)

    21.闭包 闭包:在嵌套函数内,使用非全局变量(且不使用本层变量) 闭包的作用:1.保证数据的安全性(纯洁度).2.装饰器使用 .__closure__判断是否是闭包 def func(): a = ...

  3. Python各式装饰器

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  4. 【转】Python之装饰器

    [转]Python之装饰器 本节内容 必要知识回顾 情景模拟 装饰器的概念及实现原理 回马枪(带参数的装饰器) 一. 必要知识回顾 在开始说装饰器之前,需要大家熟悉之前说过的相关知识点: 函数即“变量 ...

  5. python 闭包@装饰器

    1.装饰器 装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直 ...

  6. 浅谈Django的中间件与Python的装饰器

    浅谈Django的中间件 与Python的装饰器 一.原理 1.装饰器是Python的一种语法应用,利用闭包的原理去更改一个函数的功能,即让一个函数执行之前先到另外一个函数中执行其他需求语句,在执行该 ...

  7. python中装饰器的原理以及实现,

    python版本 3.6 1.python的装饰器说白了就是闭包函数的一种应用场景,在运用的时候我们遵循 #开放封闭原则:对修改封闭,对拓展开放 2.什么是装饰器 #装饰他人的器具,本身可以是任意可调 ...

  8. Python的装饰器实例用法小结

    这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...

  9. 13.Python略有小成(装饰器,递归函数)

    Python(装饰器,递归函数) 一.开放封闭原则 ​ 软件面世时,不可能把所有的功能都设计好,再未来的一两年功能会陆续上线,定期更新迭代,软件之前所用的源代码,函数里面的代码以及函数的调用方式一般不 ...

随机推荐

  1. Spring Boot 面试题总结

    1.什么是spring boot 答案:springboot是用来简化spring应用的初始搭建和开发过程,使用特定的配置文件来配置,例如application.properties,简化来maven ...

  2. charles 视图菜单总结

    本文参考:charles 视图菜单总结 Charles的视图菜单里的东西其实是非常常用的功能: 但是我们一般是不需要从这里点进来的: 里面,无非是查看的视图结构(按照域名和按照访问时间) 然后是一些概 ...

  3. Docker学习之docker-compose

    docker-compose 安装 1.Mac/Windows: 安装docker的时候附带安装了. 2.Linux: curl https://github.com/docker/compose L ...

  4. [Linux] Vmware 15安装CentOs后显示网络不可用

    转载自:https://blog.csdn.net/chenjin_chenjin/article/details/83617186 在虚拟机上安装好centos7后,ping www.baidu.c ...

  5. Java基础之final、static关键字

    一.前言 关于这两个关键字,应该是在开发工作中比较常见的,使用频率上来说也比较高.接口中.常量.静态方法等等.但是,使用频繁却不代表一定是能够清晰明白的了解,能说出个子丑演卯来.下面,对这两个关键字的 ...

  6. C#连接Mongo报Unable to authenticate using sasl protocol mechanism SCRAM-SHA-1错的解决方案

    ---恢复内容开始--- 最近做一个基于ABP的.net Core的项目,数据库选了MongoDB,但是返现无法给数据库设置认证,只要设置了账号密码连接就报错 连接串如下: mongodb://roo ...

  7. 使用tomcat7发布war项目启动org_apache_tomcat_websocket报错

    在使用tomcat7发布项目时(项目是用springboot 2.1.4.RELEASE版本开发的) 换成tomcat8就可以正常发布了,网上说tomcat7不支持servlet3.1,升级到spri ...

  8. 记录ceph两个rbd删除不了的处理过程

    在一个使用的环境发现两个ceph的rbd删除不了,发现两个rbd都是由于残留了watch的信息.在此记录处理过程. 处理方法 [root@node- ~]# rbd -4cce-b39d-709e05 ...

  9. TensorFlow基本计算单元与基本操作

    在学习深度学习等知识之前,首先得了解著名的框架TensorFlow里面的一些基础知识,下面首先看一下这个框架的一些基本用法. import tensorflow as tf a = 3 # Pytho ...

  10. Scala 学习笔记之集合(5)

    import collection.mutable.Buffer object CollectionDemo6 { def main(args: Array[String]): Unit = { // ...