一、最简单的装饰器

装饰器是python中很基础也很实用的一个特性。通过装饰器我们可以很方便地为一些函数添加相同的功能。我们以测量函数运行时间为例来讲一讲python装饰器的运行原理。

1、使用装饰器打印函数运行时间

通常我们使用 time 库来获取函数的运行时间。

import time

# 函数运行 2秒

def func():

time.sleep(2)

start_time = time.time()

func()

end_time = time.time()

print('函数运行时间为:%.2fs' % (end_time - start_time))

so easy,现在我们来思考一下:如果我们有 100个函数,怎么打印这100个函数的运行时间呢?

总不能按照上面的写法来100次吧,这绝不是一个程序猿应该做的事,这时候装饰器就可以排上用场了。

我们先看代码,然后再慢慢讲其中的原理。

import time

def timeit(func):

def result():

start_time = time.time()

func()

end_time = time.time()

print('函数运行时间为:%.2fs' % (end_time - start_time))

return result

@timeit

def func_0():

time.sleep(2)

# 省略98个

@timeit

def func_99():

time.sleep(2)

# 接下来直接调用这100个函数即可

func_0()

# ...

使用了 timeit 装饰器的函数和没使用装饰器的原始方法效果一样,不过这只是装饰器最简单的一个应用,我们下面来看看其中的原理。

2、timeit的运行原理

首先,大家需要知道在python中的函数也是对象,是对象就可以作为参数传递,这是装饰器实现的基础。

接下来我们来看看装饰器 timeit,不难看出 timeit 是一个函数。准确地说 timeit 是一个接受一个函数为参数并且返回一个函数的函数。

听着挺拗口的,别急,看我细细道来。

timeit 是一个函数,它接受一个参数,这个参数必须是函数(或者说可调用对象),并且 timeit 的返回结果一定是一个函数。

看到这里大家应该对装饰器有一点了解了,原来装饰器就是接受一个函数为参数返回另一个函数的函数。

现在我们就可以解释 timeit 的运行原理了,看下面的代码

# 代码块1

@timeit

def func_0():

time.sleep(2)

# 代码块2

def func_0():

time.sleep(2)

func_0 = timeit(func_0)

这里的代码块1和代码块2完全等价,注意完全两个字,这说明这两段代码的效果一模一样。

事实上代码块1就是代码块2的简写形式,因为代码块2的写法挺麻烦的,所以python的设计者加了一些语法糖,就有了代码块1的写法。

了解了这些我们再来看看 timeit 内部

def timeit(func):

def result():

start_time = time.time()

func()

end_time = time.time()

print('函数运行时间为:%.2fs' % (end_time - start_time))

return result

在 timeit里面我们定义了一个函数 result ,函数 result 里面的内容我们很熟悉,就是打印函数 func 的运行时间。这里的 func 就是我们要装饰的函数。

最后我们将函数 result 返回,我们再看到代码块2,返回的 result 函数替换了我们要装饰的函数 func_0,这时当我们再调用函数 func_0 时其实就是在调用函数 result。

3、什么是闭包

我们看一段代码:

def outer():

a = 0

def inner():

print(a)

return inner

func = outer()

func()

上面的函数和装饰器很像,以前学C语言的可能会疑惑为什么变量a在函数 outer 调用完成后仍然能够被访问。

这和python中变量的回收机制有关,python根据变量的引用计数来判断是变量是否需要回收。

当一个对象的引用被创建或复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1,如果对象的引用计数减少为0,将对象的所占用的内存释放。

上面的例子中因为对象a的引用一直没有被销毁引用计数不为0,所以在函数 inner 里一直可以访问对象a的值。

而且我们发现在调用过函数 outer 后只有函数 inner 可以访问对象a。

这就相当于为函数 inner添加了一个私有的命名空间,而且只有函数 inner 可以访问这个命名空间,这就是python中的闭包。

装饰器就是利用了闭包的原理,所以我们说装饰器就是是闭包也是完全没有问题的。

二、带参数的装饰器

我们可能看到过这样的装饰器

@decorator('arg','arg2')

def func():

# do something

pass

在一些代码中我们发现有些装饰器是有参数的,这样可以带参数的装饰器是怎么实现的呢?下面我就和大家讲一讲带参数的装饰器是怎么实现的。

在了解了装饰器的原理之后我们知道装饰器其实就是一个返回一个函数的函数。

于是我们就想:既然有返回函数的函数,那有没有返回装饰器的函数呢?当然是有的!

事实上,上面提到的带参数的装饰器就是一个返回装饰器的函数。

其中的原理也很简单,圆括号表示函数调用,而我们的程序都是从右到左执行的,所以在程序运行的时候 @ 后面的应该是 decorator 返回的装饰器。

说完原理,我们再来看看带参数的装饰器应该怎么写,先上代码。

import time

def timeit(out='函数运行时间为:%.2fs'):

def decorator(func):

def result():

start_time = time.time()

func()

end_time = time.time()

print(out % (end_time - start_time))

return result

return decorator

@timeit('函数运行时间为:%.2fs --来自带参数的装饰器')

def func():

time.sleep(2)

func() # => 函数运行时间为:2.00s --来自带参数的装饰器

我们在原来打印函数运行时间的装饰器上添加了自定义打印语句的功能,在使用的时候和下面的代码等价。

func = timeit('函数运行时间为:%.2fs --来自带参数的装饰器')(func)

带参数的装饰器也是利用了闭包将参数绑定到返回的函数上。

三、更加通用的装饰器

前面两部分讲了装饰器的原理,这一部分就讲讲要写出一个通用的装饰器需要注意的问题。

首先就是参数的问题,装饰器返回的函数不是原来的函数,函数的签名也就和原来的函数签名不一样。

当我们还是按照来的方式去调用函数就有可能会出问题,我们通过一个例子来看一下。

def decorator(func):

def result(a):

print(a)

func()

return result

@decorator

def func(a,b,c):

print(a,b,c)

func(1, 2, 3)

# => TypeError: result() takes 1 positional argument but 3 were given

这里报错是因为装饰器返回的函数只接受一个参数,我们还按照调用 func 的方式来调用 result当然会报错。

python装饰器原理-python装饰器的原理和使用相关推荐

  1. python装饰器原理-python 中的装饰器及其原理

    装饰器模式 此前的文章中我们介绍过装饰器模式: 装饰器模式中具体的 Decorator 实现类通过将对组建的请求转发给被装饰的对象,并在转发前后执行一些额外的动作来修改原有的部分行为,实现增强 Com ...

  2. python装饰器原理-Python函数装饰器原理与用法详解

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  3. python装饰器原理-Python装饰器完全解读

    1 引言 装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入理解并应用装饰器,你会更加感慨--人生苦短,我用Python. 2 初步理解装 ...

  4. python装饰器原理-Python装饰器原理

    装饰器(Decorator)是面向对象设计模式的一种,这种模式的核心思想是在不改变原来核心业务逻辑代码的情况下,对函数或类对象进行额外的修饰.python中的装饰器由python解释器直接支持,其定义 ...

  5. python装饰器实例-Python装饰器原理与简单用法实例分析

    本文实例讲述了Python装饰器原理与简单用法.分享给大家供大家参考,具体如下: 今天整理装饰器,内嵌的装饰器.让装饰器带参数等多种形式,非常复杂,让人头疼不已.但是突然间发现了装饰器的奥秘,原来如此 ...

  6. python装饰器setter实现原理_python装饰器、描述符模拟源码实现

    概要 本人python理论知识远达不到传授级别,写文章主要目的是自我总结,并不能照顾所有人,请见谅,文章结尾贴有相关链接可以作为补充 全文分为三个部分装饰器理论知识.装饰器应用.装饰器延申 装饰理基础 ...

  7. python function at 0x00000_Python函数装饰器原理与用法详解

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  8. python学习Day14 带参装饰器、可迭代对象、迭代器对象、for 迭代器工作原理、枚举对象、生成器及生成表达式...

    复习 函数的嵌套定义:在函数内部定义另一个函数 闭包:被嵌套的函数 -- 1.外层通过形参给内层函数传参 -- 2.返回内部函数对象---->  延迟执行, 开放封闭原则: 功能可以拓展,但源代 ...

  9. python函数装饰函数_Python精进-装饰器与函数对象

    本文为<爬着学Python>系列第四篇文章. 从本篇开始,本专栏在顺序更新的基础上,会有不规则的更新. 在Python的学习与运用中,我们迟早会遇到装饰器,这个概念对于初识装饰器的新手来说 ...

最新文章

  1. ORACLE11g 前期安装环境配置
  2. mysql limit锁_mysql中limit的用法
  3. Mixed Finite Elements for Variational Surface Modeling
  4. python列表去重函数_对python中两种列表元素去重函数性能的比较方法
  5. Java使用BufferedImage修改图片内容
  6. 告别乱码,针对GBK、UTF-8两种编码的智能URL解码器的java实现
  7. php 中正则表达式详解
  8. 暴力破解密码 - C++ 递归方法实现
  9. AP(接入点)模式、Router(无线路由)模式、Repeater(中继)模式、Bridge(桥接)模式、 Client(客户端)模式
  10. Kubernetes Service与Ingress详解
  11. 2021年上海市安全员C证考试报名及上海市安全员C证找解析
  12. 说说海龟交易法则的基本原理,如何实现海龟交易策略?
  13. 【放码过来】谈双重支付
  14. c++手机编程软件_积木编程软件手机版下载-积木编程软件下载v1.0.1 安卓版
  15. js解析\遍历json数据中所有的键和值
  16. SQL中where子句中不能出现聚合函数的原因
  17. js生产13位条形码
  18. 笔记本出现数字小键盘怎么解决,出现方形带斜杠123
  19. css直角线_CSS魔法堂:重拾Border之——不仅仅是圆角
  20. Mac系统下获取/创建ssh key

热门文章

  1. PHP算法 《树形结构》 之 伸展树(1) - 基本概念
  2. Objective - C基础: 第一天 - 5.对象和类
  3. CentoS 下安装gitlab
  4. 关于计算机中 原码, 反码, 补码 详解
  5. TDD, what, why, how
  6. 韩顺平java笔记 第1讲 内容介绍 项目演示 原理剖析
  7. Hexo+github搭建个人博客-博客发布篇
  8. 老男孩Python全栈开发(92天全)视频教程 自学笔记20
  9. 记录下openstack部署和使用时遇到的一些问题
  10. 阅读代码和修改别人代码的一些技巧以及注意事项