一、什么是闭包

先看一个例子:

  1. #定义一个函数
  2. def test(number):
  3. #在函数内部在定义一个函数,并且这个函数用到外围函数的变量
  4. #那么将这个函数及用到的一些变量称之为闭包
  5. def test_in(number_in):
  6. print("在test_in函数内部,number_in的值为:%d"%number_in)
  7. return number+number_in
  8. #其实这里返回的是闭包,也就是内部的函数引用
  9. return test_in
  10. #给test函数赋值,这个20就是参数number
  11. ret = test(20)
  12. #注意这里的100就是参数number_in
  13. print(ret(100))

运行结果为:

  1. test_in函数内部,number_in的值为:100
  2. 120

说明:

  • 在函数内部在定义一个函数,并且这个函数用到外围函数的变量,那么将这个函数及用到的一些变量称之为闭包
  • 在其他语言里面不允许函数内部在定义函数,但是python中的闭包可以

二、什么是装饰器

装饰器是程序开发中经常会⽤到的⼀个功能,所以这也是Python⾯试中必问的问题。

定义:

  1. 装饰器本身就是一个函数
  2. 为其他函数提供附加功能
  3. 不改变被修饰函数的源代码
  4. 不改变原调用方式
  5. 装饰器=高阶函数+嵌套函数

知识点:

  • 函数本身就是一个变量(意味着可以被复制给一个变量:test=test(1) )
  • 高阶函数:把函数名当成一个实参传递给另一个函数func(test1) (不改变源代码的前提下添加代码)
  • 返回值中包含函数名return deco (不改变函数的调用方式)
  • 嵌套函数:函数中加入新的函数

典型结构:

  1. def func(args):
  2. def func_in(args_in):
  3. pass
  4. return func_in

三、装饰器案例

1、先看一个例子

某公司有多个研发部⻔,1个基础平台部⻔,基础平台负责提供底层的功能,如:数据库操作、redis调⽤、监控API等功能。研发部⻔使⽤基础功能时,只需调⽤基础平台提供的功能即可。如下:

  1. --------------基础平台提供的功能--------------
  2. def func1():
  3. pass
  4. def func2():
  5. pass
  6. def func3():
  7. pass
  8.  
  9. --------------研发部门A使用基础平台--------------
  10. func1()
  11. func2()
  12. func3()
  13.  
  14. --------------研发部门B使用基础平台--------------
  15. func1()
  16. func2()
  17. func3()

随着项目进度的深入,产品经理提出,要在基础平台的提供的所有功能中,添加验证功能,不能谁都可以使用基础平台的全部功能,即执行功能前,先进行验证。

项目经理将此功能交给了小A去实现。

小A就去和每个研发部沟通,让每个研发部自己把验证的代码加上,结果第二天就被辞职了。

项目经理又将此功能交给了小B去实现。

小B吸取小A的经验,开始自己改代码:

  1. --------------基础平台提供的功能--------------
  2. def func1():
  3. #验证1
  4. #验证2
  5. pass
  6. def func2():
  7. #验证1
  8. #验证2
  9. pass
  10. def func3():
  11. #验证1
  12. #验证2
  13. pass
  14.  
  15. --------------研发部门A使用基础平台--------------
  16. func1()
  17. func2()
  18. func3()
  19.  
  20. --------------研发部门B使用基础平台--------------
  21. func1()
  22. func2()
  23. func3()

没过多久小B也被开除了。。。

项目经理又把工作交给了小C,小C对基础平台代码进行重构,其他业务部门无需做任何修改

  1. --------------基础平台提供的功能--------------
  2. def check_login():
  3. #验证1
  4. #验证2
  5. pass
  6.  
  7. def func1():
  8. check_login()
  9. pass
  10. def func2():
  11. check_login()
  12. pass
  13. def func3():
  14. check_login()
  15. pass
  16.  
  17. --------------研发部门A使用基础平台--------------
  18. func1()
  19. func2()
  20. func3()
  21.  
  22. --------------研发部门B使用基础平台--------------
  23. func1()
  24. func2()
  25. func3()

项目经理看后表示还不错,但是感觉还是差了一点点,于是决定不再低调,再也不让小弟做了,于是自己做了一个方案:

  1. --------------基础平台提供的功能--------------
  2. def check_login(func):
  3. def inner():
  4. #验证1
  5. #验证2
  6. func()
  7. return inner
  8.  
  9. @check_login
  10. def func1():
  11. pass
  12.  
  13. @check_login
  14. def func2():
  15. pass
  16.  
  17. @check_login
  18. def func3():
  19. pass
  20.  
  21. --------------研发部门A使用基础平台--------------
  22. func1()
  23. func2()
  24. func3()
  25.  
  26. --------------研发部门B使用基础平台--------------
  27. func1()
  28. func2()
  29. func3()

对于上述代码,也是仅仅对基础平台的代码进⾏修改,就可以实现在其他⼈调⽤函数 func1(), func2(), func3()之前都进⾏【验证】操作,并且其他研发部⻔⽆需做任何操作。

单独以func1()为例讲解:

  1. def check_login(func):
  2. def inner():
  3. #验证1
  4. #验证2
  5. func()
  6. return inner
  7.  
  8. @check_login
  9. def func1():
  10. pass

python解释器就会从上到下解释代码,步骤如下:

  1. def check_login(func): ==>将check_login函数加载到内存
  2. @check_login

没错, 从表⾯上看解释器仅仅会解释这两句代码,因为函数在没有被调⽤之前其内部代码不会被执⾏。从表⾯上看解释器着实会执⾏这两句,但是 @check_login这⼀句代码⾥却有⼤⽂章, @函数名 是python的⼀种语法糖

上例@check_login内部会执⾏⼀下操作:

执行check_login函数,并将@check_login下面的函数作为check_login函数的参数,

即@check_login等价于check_login(func1),所以内部就会去执行:

  1. def check_login(func):
  2. def inner():
  3. #验证1
  4. #验证2
  5. func() #func是参数。此时的func就是函数func1()
  6. #返回inner,inner的内部就是执行func1()函数,但是执行func1()函数前,进行了验证1,验证2
  7. return inner

check_login() 的返回值

将执行完的chenk_login函数返回值赋值 给@check_login下面的函数的函数名func1 即将check_login()的返回值再重新赋值给func1,即:

  1. func1 = def inner():
  2. #验证1
  3. #验证2
  4. func() #func是参数。此时的func就是函数func1()
  5. #返回inner,inner的内部就是执行func1()函数,但是执行func1()函数前,进行了验证1,验证2
  6. return inner

所以,以后研发部门想要执行func1函数时,就会执行新func1函数,在新func1函数内部先执行验证,再执行原来的func1函数,然后将原来func1函数的返回值返回给了业务调用者。

四、装饰器应用

  1. #定义一个装饰器:实现加粗效果
  2. def makeBold(fn):
  3. def wrapped():
  4. return "<b>"+fn()+"</b>"
  5. return wrapped
  6.  
  7. #定义一个装饰器:实现斜体效果
  8. def makeItalic(fn):
  9. def wrapped():
  10. return "<i>"+fn()+"</i>"
  11. return wrapped
  12.  
  13. #使用装饰器装饰函数
  14. @makeBold
  15. def test():
  16. return "Hello World"
  17.  
  18. #使用装饰器装饰函数
  19. @makeItalic
  20. def test1():
  21. return "Hello World"
  22.  
  23. @makeBold
  24. @makeItalic
  25. def test2():
  26. return "Hello World"
  27.  
  28. print(test())
  29. print(test1())
  30. print(test2())

运行结果为:

  1. <b>Hello World</b>
  2. <i>Hello World</i>
  3. <b><i>Hello World</i></b>

五. 装饰器示例

例1:⽆参数的函数

  1. def test_out(func):
  2. def test_in():
  3. print("name-%s"%func.__name__)
  4. func()
  5. return test_in
  6.  
  7. @test_out
  8. def test():
  9. pass
  10.  
  11. test()

运行结果为:name-test

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

  1. def test_out(func):
  2. def test_in(a,b):
  3. print(a,b)
  4. func(a,b)
  5. return test_in
  6.  
  7. @test_out
  8. def test(a,b):
  9. print("a+b=",a+b)
  10.  
  11. test(1,2)

运行结果为:

  1. 1 2
  2. a+b= 3

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

  1. def test_out(func):
  2. def test_in(*args,**kwargs):
  3. func(*args,**kwargs)
  4. return test_in
  5.  
  6. @test_out
  7. def test(*args,**kwargs):
  8. print(args,kwargs)
  9.  
  10. test(1)
  11. test(1,2)
  12. test(1,2,3,k="v")

运行结果为:

  1. (1,) {}
  2. (1, 2) {}
  3. (1, 2, 3) {'k': 'v'}

说明:如果被修饰的函数有参数,则装饰器内部的函数也要有同样个数的参数才可以匹配成功。

例4:装饰器中的return

  1. def test_out(func):
  2. def test_in():
  3. func()
  4. return test_in
  5.  
  6. @test_out
  7. def test():
  8. return "hello"
  9.  
  10. result = test()
  11. print(result)

运行结果为:None

如果修改装饰器为 return func() ,则运⾏结果:

  1. def test_out(func):
  2. def test_in():
  3. return func()
  4. return test_in
  5.  
  6. @test_out
  7. def test():
  8. return "hello"
  9.  
  10. result = test()
  11. print(result)

运行结果为:hello

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

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

六、类装饰器

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

  1. class Test():
  2. def __call__(self):
  3. print("call me")
  4.  
  5. t = Test()
  6. t()

执行结果:call me

类装饰器demo

  1. class Test():
  2. def __init__(self,func):
  3. print("----初始化----")
  4. print("func name is %s"%func.__name__)
  5. self.__func = func
  6.  
  7. def __call__(self):
  8. print("----类装饰器的功能----")
  9. self.__func()
  10. print("----类装饰器执行完毕----")
  11. @Test
  12. def test():
  13. print("----test----")
  14.  
  15. test()

运行结果为:

  1. ----初始化----
  2. func name is test
  3. ----类装饰器的功能----
  4. ----test----
  5. ----类装饰器执行完毕----

说明:

  • 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
  • 并且会把test这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量执行了test函数体
  • test函数相当于指向了用Test创建出来的实例对象
  • 挡在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
  • 为了能够在__call__方法中调用原来的test指向的函数体,所以在__init__方法中就需要一个实例属性保存这个引用,所以才有了self.__func = func这句代码,从而在调用__call__方法中就能调用test

python高级-装饰器(19)的更多相关文章

  1. Python函数装饰器高级用法

    在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...

  2. 【转】详解Python的装饰器

    原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现 ...

  3. 详解Python的装饰器

    Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def sa ...

  4. Python的装饰器实例用法小结

    这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...

  5. 进阶Python:装饰器 全面详解

    进阶Python:装饰器 前言 前段时间我发了一篇讲解Python调试工具PySnooper的文章,在那篇文章开始一部分我简单的介绍了一下装饰器,文章发出之后有几位同学说"终于了解装饰器的用 ...

  6. 我终于弄懂了Python的装饰器(二)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我终于弄懂了Python的装饰器(三) 4. 我终于弄懂了Python的装饰器(四) 二 ...

  7. 我终于弄懂了Python的装饰器(四)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我终于弄懂了Python的装饰器(三) 4. 我终于弄懂了Python的装饰器(四) 四 ...

  8. Python各式装饰器

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  9. Python札记 -- 装饰器补充

    本随笔是对Python札记 -- 装饰器的一些补充. 使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码: #!/usr/bin/env python def deco(func): def ...

随机推荐

  1. Flink on yarn的配置及执行

    1. 写在前面 Flink被誉为第四代大数据计算引擎组件,即可以用作基于离线分布式计算,也可以应用于实时计算.Flink可以自己搭建集群模式已提供为庞大数据的计算.但在实际应用中.都是计算hdfs上的 ...

  2. 关于spring的自动注入

    关于spring的自动注入 spring里面可以设置BeanDefinition自动注入类型,默认为AUTOWIRE_NO(不进行自动注入).mybatis里面的扫描接口生成MapperFactory ...

  3. python_web框架

    一.web框架 web框架: 自己完成socket的web框架:如,Tornado等 由WSGI完成socket的web框架:如,Django.flash等 两种实现过程: 第二种WSGI方式的,由于 ...

  4. spark ML pipeline 学习

    一.pipeline 一个典型的机器学习过程从数据收集开始,要经历多个步骤,才能得到需要的输出.这非常类似于流水线式工作,即通常会包含源数据ETL(抽取.转化.加载),数据预处理,指标提取,模型训练与 ...

  5. Git帮助之初始化项目设置向导

    初始化项目设置向导 Git设置: git config --global user.name "Your Name Here" # 设置Git提交时的默认用户名,推荐使用本站用户名 ...

  6. 数据导出Excel表格

    public String exportInfoFr(String path,String name,String startdate,String enddate,SysUser user){ Li ...

  7. DAY1 VS2017&CUDA10.01环境搭建

    Visual Studio工程配置情况: VC++目录配置: C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.\common\lib\x64 C: ...

  8. C++ 初读vector

    vector 向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container).跟任意其它类型容器一样,它能够存放各种类型的对象. Character 高效 C++标准要 ...

  9. 《Java项目中classpath路径详解》

    项目里用到了classpath路径来引用文件,那么classpath指的是哪里呢 我首先把上面的applicationContext.xml文件放在了src目录下发现可以. 那么classpath到底 ...

  10. C#的排序Sort和OrderBy扩展方法

    可以实现一个IComparable接口的CompareTo方法,或者是给予List的Sort扩展方法,传入委托实现,举个例子: list.Sort((a, b) => { var o = a.s ...