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动态加载包的方法小结
Apr 18 Python
Python 中的range(),以及列表切片方法
Jul 02 Python
python数据挖掘需要学的内容
Jun 23 Python
8种用Python实现线性回归的方法对比详解
Jul 10 Python
python3的print()函数的用法图文讲解
Jul 16 Python
Pytorch Tensor的索引与切片例子
Aug 18 Python
python爬虫 urllib模块url编码处理详解
Aug 20 Python
python conda操作方法
Sep 11 Python
python调用HEG工具批量处理MODIS数据的方法及注意事项
Feb 18 Python
Python通过正则库爬取淘宝商品信息代码实例
Mar 02 Python
Django正则URL匹配实现流程解析
Nov 13 Python
Python爬虫 简单介绍一下Xpath及使用
Apr 26 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
颠覆常识!无色透明的咖啡诞生了(中日双语)
2021/03/03 咖啡文化
php实现自定义中奖项数和概率的抽奖函数示例
2017/05/26 PHP
PJBlog插件 防刷新的在线播放器
2006/10/25 Javascript
document.documentElement &amp;&amp; document.documentElement.scrollTop
2007/12/01 Javascript
JQuery的Alert消息框插件使用介绍
2010/10/09 Javascript
javascript 拖动表格行实现代码
2011/05/05 Javascript
jquery中ajax学习笔记3
2011/10/16 Javascript
jQuery 选择表格(table)里的行和列及改变简单样式
2012/12/15 Javascript
javascript实现延时显示提示框特效代码
2016/04/27 Javascript
JS获取本周周一,周末及获取任意时间的周一周末功能示例
2017/02/09 Javascript
js判断PC端与移动端跳转
2020/12/24 Javascript
AngularJS 仿微信图片手势缩放的实例
2017/09/28 Javascript
js实现复制功能(多种方法集合)
2018/01/06 Javascript
angularjs实现table增加tr的方法
2018/02/27 Javascript
微信小程序引用iconfont图标的方法
2018/10/22 Javascript
vue响应式系统之observe、watcher、dep的源码解析
2019/04/09 Javascript
VueJS 取得 URL 参数值的方法
2019/07/19 Javascript
vue路由教程之静态路由
2019/09/03 Javascript
element el-tree组件的动态加载、新增、更新节点的实现
2020/02/27 Javascript
Vue实现简单的跑马灯
2020/05/25 Javascript
Vue页面手动刷新,实现导航栏激活项还原到初始状态
2020/08/06 Javascript
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
[01:11:48]Fnatic vs IG 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
Python批量转换文件编码格式
2015/05/17 Python
python基础教程之匿名函数lambda
2017/01/17 Python
python3库numpy数组属性的查看方法
2018/04/17 Python
Python generator生成器和yield表达式详解
2019/08/08 Python
虚拟机下载python是否需要联网
2020/07/27 Python
解决pip安装的第三方包在PyCharm无法导入的问题
2020/10/15 Python
全球性的在线鞋类品牌:Public Desire
2019/04/03 全球购物
初二物理教学反思
2014/01/29 职场文书
总经理任命书
2014/03/29 职场文书
商场主管竞聘书
2014/03/31 职场文书
文秘个人求职信范文
2014/04/22 职场文书
项目投资建议书
2014/05/16 职场文书
浅析CSS在DevTools 中架构演变
2021/10/05 HTML / CSS