Python闭包和装饰器用法实例详解


Posted in Python onMay 22, 2019

本文实例讲述了Python闭包和装饰器用法。分享给大家供大家参考,具体如下:

Python的装饰器的英文名叫Decorator,作用是完成对一些模块的修饰。所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去。

闭包

1.函数引用

#coding=utf-8
def test1():
  print('This is test1!')
#调用函数
test1()
#引用函数
ret = test1
#打印id
print('test1\t的地址:',id(test1))
print('ret\t\t的地址:',id(ret))
print('你会发现test1的地址和ret的地址是一样的!')
#通过引用调用函数
ret()

运行结果:

This is test1!
test1   的地址: 139879303947128
ret     的地址: 139879303947128
你会发现test1的地址和ret的地址是一样的!
This is test1!

1. 什么是闭包

在嵌套函数中,内部函数用到了外部函数的变量,则

称内部函数为闭包。

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

上代码:

#coding=utf-8
def outer(num):
  def inner(num_in):
    return num + num_in
  return inner
#10赋值给了num
ret = outer(10)
#20赋值给了num_in
print('ret(20) = ',ret(20))
#30赋值给了num_in
print('ret(30) = ',ret(30))

运行结果:

ret(20) =  30
ret(30) =  40

闭包的应用例子一:

看代码:

#coding=utf-8
def line_conf(a, b):
  def line(x):
    return a*x + b
  return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))

运行结果:

6
25

这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

闭包思考:

1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成。
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。

代码如下:

#coding=utf-8
#定义函数:完成包裹数据
def makeBold(func):
  def wrapped():
    return "<b>" + func() + "</b>"
  return wrapped
#定义函数:完成包裹数据
def makeItalic(fn):
  def wrapped():
    return "<i>" + fn() + "</i>"
  return wrapped
@makeBold
def test1():
  return "hello world-1"
@makeItalic
def test2():
  return "hello world-2"
@makeBold
@makeItalic
def test3():
  return "hello world-3"
print(test1())
print(test2())
print(test3())

运行结果:

<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>

装饰器(decorator)功能

1. 引入日志
2. 函数执行时间统计
3. 执行函数前预备处理
4. 执行函数后清理功能
5. 权限校验等场景
6. 缓存

装饰器示例

例1:无参数的函数

代码如下:

#coding=utf-8
from time import ctime, sleep
def time_func(func):
  def wrapped_func():
    print('%s call at %s'%(func.__name__, ctime()))
    func()
  return wrapped_func
@time_func
def foo():
  print('i am foo!')
foo()
sleep(2)
foo()

运行结果:

foo call at Thu Aug 24 21:32:39 2017
i am foo!
foo call at Thu Aug 24 21:32:41 2017
i am foo!

例2:被装饰的函数有参数

#coding=utf-8
from time import ctime, sleep
def timefunc(func):
  def wrappedfunc(a, b):
    print('%s called at %s'%(func.__name__, ctime()))
    print(a, b)
    func(a, b)
  return wrappedfunc
@timefunc
def foo(a,b):
  print(a+b)
foo(3,5)
sleep(2)
foo(2,4)

运行结果:

foo called at Thu Aug 24 21:40:20 2017
3 5
8
foo called at Thu Aug 24 21:40:22 2017
2 4
6

例3:被装饰的函数有不定长参数

#coding=utf-8
from time import ctime, sleep
def timefunc(func):
  def wrappedfunc(*args, **kwargs):
    print('%s called at %s'%(func.__name__, ctime()))
    func(*args, **kwargs)
  return wrappedfunc
@timefunc
def foo(a,b,c):
  print(a+b+c)
foo(3,5,7)
sleep(2)
foo(2,4,9)

运行结果:

foo called at Thu Aug 24 21:45:13 2017
15
foo called at Thu Aug 24 21:45:15 2017
15

例4:装饰器中的return

如下:

#coding=utf-8
from time import ctime
def timefunc(func):
  def wrappedfunc():
    print('%s called at %s'%(func.__name__, ctime()))
    func()
  return wrappedfunc
@timefunc
def getInfo():
  return '---hello---'
info = getInfo()
print(info)

代码如下:

getInfo called at Thu Aug 24 21:59:26 2017
None

如果修改装饰器为 return func():

如下:

#coding=utf-8
from time import ctime
def timefunc(func):
  def wrappedfunc():
    print('%s called at %s'%(func.__name__, ctime()))
    return func()
  return wrappedfunc
@timefunc
def getInfo():
  return '---hello---'
info = getInfo()
print(info)

代码如下:

getInfo called at Thu Aug 24 22:07:12 2017
---hello---

总结:

一般情况下为了让装饰器更通用,可以有return

例5:装饰器带参数,在原有装饰器的基础上,设置外部变量

#coding=utf-8
from time import ctime, sleep
def timefun_arg(pre="hello"):
  def timefunc(func):
    def wrappedfunc():
      print('%s called at %s'%(func.__name__, ctime()))
      return func()
    return wrappedfunc
  return timefunc
@timefun_arg('hello')
def foo1():
  print('i am foo')
@timefun_arg('world')
def foo2():
  print('i am foo')
foo1()
sleep(2)
foo1()
foo2()
sleep(2)
foo2()

运行结果:

foo1 called at Thu Aug 24 22:17:58 2017
i am foo
foo1 called at Thu Aug 24 22:18:00 2017
i am foo
foo2 called at Thu Aug 24 22:18:00 2017
i am foo
foo2 called at Thu Aug 24 22:18:02 2017
i am foo

可以理解为:

foo1()==timefun_arg("hello")(foo1())
foo2()==timefun_arg("world")(foo2())

例6:类装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了 call() 方法,那么这个对象就是

callable的。
class Test():
  def __call__(self):
    print('call me!')
t = Test()
t() # call me

类装饰器demo:

class Decofunc(object):
  def __init__(self, func):
    print("--初始化--")
    self._func = func
  def __call__(self):
    print('--装饰器中的功能--')
    self._func()
@Decofunc
def showpy():
  print('showpy')
showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--"

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
编写Python脚本批量下载DesktopNexus壁纸的教程
May 06 Python
python3.5使用tkinter制作记事本
Jun 20 Python
Python利用flask sqlalchemy实现分页效果
Aug 02 Python
python实现支付宝当面付(扫码支付)功能
May 30 Python
python使用PIL给图片添加文字生成海报示例
Aug 17 Python
几行Python代码爬取3000+上市公司的信息
Jan 24 Python
python实现扑克牌交互式界面发牌程序
Apr 22 Python
python中rb含义理解
Jun 18 Python
Matplotlib.pyplot 三维绘图的实现示例
Jul 28 Python
matplotlib 使用 plt.savefig() 输出图片去除旁边的空白区域
Jan 05 Python
python如何在word中存储本地图片
Apr 07 Python
Python 中的单分派泛函数你真的了解吗
Jun 22 Python
Python进程间通信Queue消息队列用法分析
May 22 #Python
将python文件打包成EXE应用程序的方法
May 22 #Python
Python多线程threading模块用法实例分析
May 22 #Python
Python3之手动创建迭代器的实例代码
May 22 #Python
PyTorch搭建一维线性回归模型(二)
May 22 #Python
PyTorch基本数据类型(一)
May 22 #Python
PyTorch搭建多项式回归模型(三)
May 22 #Python
You might like
社区(php&amp;&amp;mysql)三
2006/10/09 PHP
php基础知识:类与对象(2) 自动加载对象
2006/12/13 PHP
js限制checkbox勾选的个数以及php获取多个checkbbox的方法深入解析
2013/07/18 PHP
php异步多线程swoole用法实例
2014/11/14 PHP
关于php微信订阅号开发之token验证后自动发送消息给订阅号但是没有消息返回的问题
2015/12/21 PHP
PHP使用trim函数去除字符串左右空格及特殊字符实例
2016/01/07 PHP
PHP5.2中PDO的简单使用方法
2016/03/25 PHP
CodeIgniter框架数据库基本操作示例
2018/05/24 PHP
使用jQuery插件创建常规模态窗口登陆效果
2013/08/23 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
2015/10/12 Javascript
javascript日期格式化方法小结
2015/12/17 Javascript
jQuery Form 表单提交插件之formSerialize,fieldSerialize,fieldValue,resetForm,clearForm,clearFields的应用
2016/01/23 Javascript
浅谈 Vue 项目优化的方法
2017/12/16 Javascript
layui的table单击行勾选checkbox功能方法
2018/08/14 Javascript
Js on及addEventListener原理用法区别解析
2020/07/11 Javascript
python和C语言混合编程实例
2014/06/04 Python
Python实现批量修改文件名实例
2015/07/08 Python
Python基于checksum计算文件是否相同的方法
2015/07/09 Python
python的Tqdm模块的使用
2018/01/10 Python
Python创建字典的八种方式
2019/02/27 Python
浅谈Django+Gunicorn+Nginx部署之路
2019/09/11 Python
基于nexus3配置Python仓库过程详解
2020/06/15 Python
python基于pygame实现飞机大作战小游戏
2020/11/19 Python
彪马土耳其官网:PUMA土耳其
2019/07/14 全球购物
十佳青年个人事迹材料
2014/01/28 职场文书
入党申请自荐书范文
2014/02/11 职场文书
行政内勤岗位职责
2014/04/07 职场文书
武侯祠导游词
2015/02/04 职场文书
周末问候语大全
2015/11/10 职场文书
小学数学继续教育研修日志
2015/11/13 职场文书
如何制作自己的原生JavaScript路由
2021/05/05 Javascript
用React Native制作一个简单的游戏引擎
2021/05/27 Javascript
Python爬虫之自动爬取某车之家各车销售数据
2021/06/02 Python
详解Vue3使用axios的配置教程
2022/04/29 Vue.js
html中两种获取标签内的值的方法
2022/06/10 HTML / CSS
Java 多线程并发FutureTask
2022/06/28 Java/Android