一、什么是闭包

先看一个例子:

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

运行结果为:

在test_in函数内部,number_in的值为:100
120

说明:

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

二、什么是装饰器

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

定义:

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

知识点:

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

典型结构:

def func(args):
def func_in(args_in):
pass
return func_in

三、装饰器案例

1、先看一个例子

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

--------------基础平台提供的功能--------------
def func1():
pass
def func2():
pass
def func3():
pass --------------研发部门A使用基础平台--------------
func1()
func2()
func3() --------------研发部门B使用基础平台--------------
func1()
func2()
func3()

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

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

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

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

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

--------------基础平台提供的功能--------------
def func1():
#验证1
#验证2
pass
def func2():
#验证1
#验证2
pass
def func3():
#验证1
#验证2
pass --------------研发部门A使用基础平台--------------
func1()
func2()
func3() --------------研发部门B使用基础平台--------------
func1()
func2()
func3()

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

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

--------------基础平台提供的功能--------------
def check_login():
#验证1
#验证2
pass def func1():
check_login()
pass
def func2():
check_login()
pass
def func3():
check_login()
pass --------------研发部门A使用基础平台--------------
func1()
func2()
func3() --------------研发部门B使用基础平台--------------
func1()
func2()
func3()

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

--------------基础平台提供的功能--------------
def check_login(func):
def inner():
#验证1
#验证2
func()
return inner @check_login
def func1():
pass @check_login
def func2():
pass @check_login
def func3():
pass --------------研发部门A使用基础平台--------------
func1()
func2()
func3() --------------研发部门B使用基础平台--------------
func1()
func2()
func3()

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

单独以func1()为例讲解:

def check_login(func):
def inner():
#验证1
#验证2
func()
return inner @check_login
def func1():
pass

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

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

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

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

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

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

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

check_login() 的返回值

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

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

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

四、装饰器应用

#定义一个装饰器:实现加粗效果
def makeBold(fn):
def wrapped():
return "<b>"+fn()+"</b>"
return wrapped #定义一个装饰器:实现斜体效果
def makeItalic(fn):
def wrapped():
return "<i>"+fn()+"</i>"
return wrapped #使用装饰器装饰函数
@makeBold
def test():
return "Hello World" #使用装饰器装饰函数
@makeItalic
def test1():
return "Hello World" @makeBold
@makeItalic
def test2():
return "Hello World" print(test())
print(test1())
print(test2())

运行结果为:

<b>Hello World</b>
<i>Hello World</i>
<b><i>Hello World</i></b>

五. 装饰器示例

例1:⽆参数的函数

def test_out(func):
def test_in():
print("name-%s"%func.__name__)
func()
return test_in @test_out
def test():
pass test()

运行结果为:name-test

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

def test_out(func):
def test_in(a,b):
print(a,b)
func(a,b)
return test_in @test_out
def test(a,b):
print("a+b=",a+b) test(1,2)

运行结果为:

1 2
a+b= 3

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

def test_out(func):
def test_in(*args,**kwargs):
func(*args,**kwargs)
return test_in @test_out
def test(*args,**kwargs):
print(args,kwargs) test(1)
test(1,2)
test(1,2,3,k="v")

运行结果为:

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

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

例4:装饰器中的return

def test_out(func):
def test_in():
func()
return test_in @test_out
def test():
return "hello" result = test()
print(result)

运行结果为:None

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

def test_out(func):
def test_in():
return func()
return test_in @test_out
def test():
return "hello" result = test()
print(result)

运行结果为:hello

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

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

六、类装饰器

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

class Test():
def __call__(self):
print("call me") t = Test()
t()

执行结果:call me

类装饰器demo

class Test():
def __init__(self,func):
print("----初始化----")
print("func name is %s"%func.__name__)
self.__func = func def __call__(self):
print("----类装饰器的功能----")
self.__func()
print("----类装饰器执行完毕----")
@Test
def test():
print("----test----") test()

运行结果为:

----初始化----
func name is test
----类装饰器的功能----
----test----
----类装饰器执行完毕----

说明:

  • 当用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. C# Conversion Keywords

    主要是解决类与其他不同数据类型的转换 类于类的显式转换: explicit  public static explicit operator B(A a) { return new B(){a.... ...

  2. Spring boot实现原生websocket

    网上的大部分教程是基于sockjs,这篇文章内容则是基于原生协议. 后台Spring boot 配置 @Configuration @EnableWebSocket public class WebS ...

  3. mybatis和hibernate中的懒加载

    概念:所谓懒加载就是延时加载,延迟加载.什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载.至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限 ,为了减 ...

  4. 机器学习之--kmeans聚类简单算法实例

    import numpy as np import sklearn.datasets #加载原数据 import matplotlib.pyplot as plt import random #点到各 ...

  5. SpringBoot使用ELK日志收集

    本文介绍SpringBoot应用配合ELK进行日志收集. 1.有关ELK 1.1 简介 在之前写过一篇文章介绍ELK日志收集方案,感兴趣的可以去看一看,点击这里-----> <ELK日志分 ...

  6. OO Unit 2 电梯调度

    目录 OO Unit2 博客作业 基于度量来分析⾃己的程序结构 复杂度分析 架构分析 改进和重构 发现过的BUG 简化问题 多线程初探 OO Unit2 博客作业 基于度量来分析⾃己的程序结构 自认为 ...

  7. 自己编译Android(小米5)内核并刷入(一键自动编译打包)

    之前自己编译过Android系统,刷入手机.编译很简单,但坑比较大,主要是GFW埋的坑.. 编译android系统太大了,今天记下自己编译及刷入android内核的方法. 主要是看到第三方内核可以超频 ...

  8. iview修改tabbar实现小程序自定义中间圆形导航栏及多页面登录功能

    emmm,用iview改了个自定义中间圆形的tabbar. 如下图所示, 重点,什么鬼是“多页面登录”? 例如:我现在要做一个功能,要说自己长得帅才能进去页面. 一个两个页面还好,但是我现在要每个页面 ...

  9. C# yield return 和 yield break

    yield关键字用于遍历循环中,yield return用于返回IEnumerable<T>,yield break用于终止循环遍历. 以下对比了使用yield return与不使用yie ...

  10. Java for Andriod 第二周学习总结

    第四章 学习时遇到的问题或新知识点: 1. 构造方法.每个类至少有一个构造方法,且构造方法必须的名称必须与类名相同. 2. Varargs.允许方法拥有一个可变长度的参数列表. 3. 对象的内存分配. ...