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系列之教你如何根据图片生成字符画
May 23 Python
python获取当前用户的主目录路径方法(推荐)
Jan 12 Python
Request的中断和ErrorHandler实例解析
Feb 12 Python
使用Django连接Mysql数据库步骤
Jan 15 Python
python3.x实现base64加密和解密
Mar 28 Python
Django页面数据的缓存与使用的具体方法
Apr 23 Python
Python 绘制酷炫的三维图步骤详解
Jul 12 Python
python中for循环把字符串或者字典添加到列表的方法
Jul 20 Python
Python 迭代,for...in遍历,迭代原理与应用示例
Oct 12 Python
python 函数嵌套及多函数共同运行知识点讲解
Mar 03 Python
django 取消csrf限制的实例
Mar 13 Python
Django实现内容缓存实例方法
Jun 30 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的cms
2010/12/19 PHP
PHP正则表达式 /i, /is, /s, /isU等介绍
2014/10/23 PHP
thinkphp中html:list标签传递多个参数实例
2014/10/30 PHP
Linux系统递归生成目录中文件的md5的方法
2015/06/29 PHP
PHP面向对象程序设计之对象克隆clone和魔术方法__clone()用法分析
2019/06/12 PHP
php模拟post提交请求调用接口示例解析
2020/08/07 PHP
jQuery 入门级学习笔记及源码
2010/01/22 Javascript
innerHTML 和 getElementsByName 在IE下面的bug 的解决
2010/04/09 Javascript
动态加载脚本提升javascript性能
2014/02/24 Javascript
js比较日期大小的方法
2015/05/12 Javascript
JS实现的仿东京商城菜单、仿Win右键菜单及仿淘宝TAB特效合集
2015/09/28 Javascript
在页面中输出当前客户端时间javascript实例代码
2016/03/02 Javascript
通过网页查看JS源码中汉字显示乱码的解决方法
2016/10/26 Javascript
JS实现标签滚动切换效果
2017/12/25 Javascript
关于uniApp editor微信滑动问题
2021/01/15 Javascript
Python 字典dict使用介绍
2014/11/30 Python
举例详解Python中循环语句的嵌套使用
2015/05/14 Python
详解Python中的日志模块logging
2015/06/19 Python
pandas 条件搜索返回列表的方法
2018/10/30 Python
python实现浪漫的烟花秀
2019/01/30 Python
解决python3中的requests解析中文页面出现乱码问题
2019/04/19 Python
有趣的Python图片制作之如何用QQ好友头像拼接出里昂
2020/04/22 Python
python退出循环的方法
2020/06/18 Python
Python pip install之SSL异常处理操作
2020/09/03 Python
Python+OpenCV检测灯光亮点的实现方法
2020/11/02 Python
浅谈盘点5种基于Python生成的个性化语音方法
2021/02/05 Python
Python爬虫爬取ts碎片视频+验证码登录功能
2021/02/22 Python
浅析HTML5中的download属性使用
2019/03/13 HTML / CSS
英国最大的香水商店:The Fragrance Shop
2018/07/06 全球购物
施工人员岗位职责
2013/12/12 职场文书
前台文员岗位职责
2013/12/28 职场文书
工厂会计员职责
2014/02/06 职场文书
法人授权委托书范本
2014/04/04 职场文书
求职意向书
2014/07/29 职场文书
大学团日活动新闻稿
2014/09/10 职场文书
2015年五一劳动节演讲稿
2015/03/18 职场文书