闭包

引入

  • 函数名是一个特殊的变量,保存了函教的地址和
  • 自定义一个变量可以获取函数地址
  • 自定义变量调用函数 “函数名()”
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def work():
    print("我是一个测试函数")


ret = work
print(ret)

print("地址:%X" % id(ret))
print("地址:%X" % id(work))

ret()
work()

out:
    <function work at 0x00000161E3091E18>
    地址161E3091E18
    地址161E3091E18
    我是一个测试函数
    我是一个测试函数
  • 当我们定义函数test1后,函数名test1保存的是函数在内存的首地址函数名就是一个特殊的变量,函数名()调用函数,执行函数体test1()ret()都会调用函数,并执行函数体
  • 和变量名一样的,函数名数名只是函数代码空间的引用,当函数名赋值给一个对象的时候就是引用传递。

闭包概念

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包

维基百科中关于闭包的概念:在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

闭包构成条件

  • 必须有一个内嵌函数(函数里定义的函数)–这对应函数之间的嵌套
  • 内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量–内部函数引用外部变量
  • 外部函数必须返回内嵌函数–必须返回那个内部函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def function_out(num):
    print("1.function_out num = ", num)

    def function_in(num_in):
        print("2.---------- function_in -----------------num", num)
        print("3.---------- function_in -----------------num_in", num_in)

    return function_in


# function_out(10)
# 调用function_out获取内层函数的地址,保存到ret
ret = function_out(100)

# 调用内层函数
ret(88)

out:
    1.function_out num =  100
    2.---------- function_in -----------------num 100
    3.---------- function_in -----------------num_in 88

通俗理解:

  • 存在函数的嵌套关系
  • 内层函数引用了外层函数的临时变量
  • 外层函数返回内层函数的引用

image-20191117154908036

闭包中的变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def function_out(num):

    def function_in():
        # 如果在内层定义了和外层变量同名的变量,但是要使用外层变量
        # nonlocal 不使用内层函数的,而是使用外层的变量
        nonlocal num

        print("function_in num", num)
        
        # 内部自定义的变量
        num = 88

    return function_in


# 调用外部函数
ret = function_out(99)
ret()

out:
    function_in num 99

装饰器入门

  • 装饰器作用:在不改变函数的代码前提下,给函数添加新的功能
  • 装饰器的使用:
    • 存在闭包
    • 需要装饰的函数

写代码要道循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的勤能代码块
  • 开放:对扩展开放
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def function_out(func):
    def function_in():
        print("开始验证")
        func()

    return function_in


@function_out
# @function_out装饰了 login() 函数
# 底层实现
# login = function_out(login)
def login():
    print("开始登陆!")

# 通过闭包调用外层函数
# login = function_out(login)
login()

out:
	开始验证
    开始登陆
  • 通用版装饰器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def function_out(func):

    def function_in(*args, **kwargs):
        print("开始验证")
        print("function_in user=", args)
        print("function_in password=", kwargs)
        return  func(*args, **kwargs)

    return function_in


@function_out
def login(*args, **kwargs):
    print("开始登陆 user=", args)
    print("开始登陆 password=", kwargs)

    return 10

# 装饰完 login == function_in
result = login(20, a=18)
print(result)

out:
    开始验证
    function_in user= (20,)
    function_in password= {'a': 18}
    开始登陆 user= (20,)
    开始登陆 password= {'a': 18}
    10

在原装饰器增加外部变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

def test(path):
    print(path)
    
    def function_out(func):

        def function_in():
            print("开始验证")
            func()

        return function_in
    # 返回装饰器的引用(装饰器工厂)
    return function_out


@test("login.py")
# @test("login.py")分解为2步
# 1)test("login.py")-->function out 引用(地址)
# 2)@ 第一步的结果-->@function out
def login():
    print("开始登陆")


@test("register.py")
def register():
    print("开始注册")


login()
register()

out:
	login.py
    register.py
    开始验证
    开始登陆
    开始验证
    开始注册

多重装饰器

  • 给一个函数进行多次装饰
  • 装饰原则:就近原则(靠近待装饰函数的先装饰,随后一层一层装饰)

多重装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# <b>helloWord</b>
# 定义一个让文字加粗的装饰器
def makeBlod(func):

    def function_in():
        return '<b>' + func() + '</b>'
    return function_in


# 定义一个让文字倾斜的装饰器
def makeItalic(func):

    def function_in():
        return '<i>' + func() + '</i>'
    return function_in


@makeBlod
def demo():
    return "helloWord"


@makeItalic
def demo2():
    return "Luenci"


@makeBlod
@makeItalic
def demo3():
    return "LYnn"


print(demo())
print(demo2())
print(demo3())


out:
	<b>helloWord</b>
    <i>Luenci</i>
    <b><i>LYnn</i></b>

类装饰器

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

  • 类的书写:

  • 必须有两个方法

    • 1)__init__ 方法,必须接收装饰器传递的参数func
  • 2)__call__方法

    • 格式:
    • @类名
      • 待装饰的函数 对象名()调用对象的__call__()方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 装饰器类
class Demo(object):

    def __init__(self, func):
        print("__init__方法")
        print("-- func --", func)
        self.func = func

    def run(self):
        print("正在奔跑")

    def __call__(self, *args, **kwargs):
        print("-- 开始验证 --")
        # 调用原来login内容
        self.func()


@Demo
# login = demo(login)
def login():
    print("正在登陆")


login()

out:
    __init__方法
    -- func -- <function login at 0x0000024E64D857B8>
    -- 开始验证 --
    正在登陆