一, 什么是闭包
先看一个例子:
- # 定义一个函数
- 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 试中必问的问题.
定义:
装饰器本身就是一个函数
为其他函数提供附加功能
不改变被修饰函数的源代码
不改变原调用方式
装饰器 = 高阶函数 + 嵌套函数
知识点:
函数本身就是一个变量 (意味着可以被复制给一个变量: 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
来源: https://www.cnblogs.com/Se7eN-HOU/p/10724523.html