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使用urllib2获取网络资源实例讲解
Dec 02 Python
Python实现打印螺旋矩阵功能的方法
Nov 21 Python
利用Python暴力破解zip文件口令的方法详解
Dec 21 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
Dec 06 Python
对django 模型 unique together的示例讲解
Aug 06 Python
python正则爬取某段子网站前20页段子(request库)过程解析
Aug 10 Python
python 计算积分图和haar特征的实例代码
Nov 20 Python
Python callable内置函数原理解析
Mar 05 Python
Python GUI编程学习笔记之tkinter界面布局显示详解
Mar 30 Python
基于python和flask实现http接口过程解析
Jun 15 Python
python使用建议与技巧分享(一)
Aug 17 Python
Pytorch 使用tensor特定条件判断索引
Apr 08 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格式化工具Beautify PHP小小BUG
2008/04/24 PHP
php学习笔记之面向对象
2014/11/08 PHP
使用PHP如何实现高效安全的ftp服务器(二)
2015/12/30 PHP
浅谈php中的循环while、do...while、for、foreach四种循环
2016/11/05 PHP
PHP 中TP5 Request 请求对象的实例详解
2017/07/31 PHP
laravel中的一些简单实用功能
2018/11/03 PHP
BOOM vs RR BO5 第一场 2.14
2021/03/10 DOTA
javascript 去字符串空格终极版(支持utf8)
2009/11/14 Javascript
JavaScript 序列化对象实现代码
2009/12/18 Javascript
javascript的字符串按引用复制和传递,按值来比较介绍与应用
2012/12/28 Javascript
利用jQuery的deferred对象实现异步按顺序加载JS文件
2013/03/17 Javascript
Node.js实现数据推送
2016/04/14 Javascript
基于BootStarp的Dailog
2016/04/28 Javascript
js 打开新页面在屏幕中间的实现方法
2016/11/02 Javascript
angularjs定时任务的设置与清除示例
2017/06/02 Javascript
ES6新增数据结构WeakSet的用法详解
2017/08/07 Javascript
angularJS实现动态添加,删除div方法
2018/02/27 Javascript
微信小程序中遇到的iOS兼容性问题小结
2018/11/14 Javascript
解决vue组件中click事件失效的问题
2019/11/09 Javascript
Python 连连看连接算法
2008/11/22 Python
使用graphics.py实现2048小游戏
2015/03/10 Python
解决Python中由于logging模块误用导致的内存泄露
2015/04/23 Python
使用tqdm显示Python代码执行进度功能
2019/12/08 Python
使用python 的matplotlib 画轨道实例
2020/01/19 Python
python实现录屏功能(亲测好用)
2020/03/02 Python
data:image data url 文件转为Blob上传后端的方法
2019/07/16 HTML / CSS
缓解脚、腿和背部疼痛:Z-CoiL鞋
2019/03/12 全球购物
SQL Server面试题
2016/10/17 面试题
《台湾的蝴蝶谷》教学反思
2014/02/20 职场文书
活动总结格式
2014/08/30 职场文书
企业群众路线教育实践活动心得体会
2014/11/03 职场文书
2014司机年终工作总结
2014/12/05 职场文书
草房子读书笔记
2015/06/29 职场文书
2016高考寄语集锦
2015/12/04 职场文书
《走遍天下书为侣》教学反思
2016/02/22 职场文书
golang json数组拼接的实例
2021/04/28 Golang