闭包

1. 函数对象

精髓: 可以把函数当成变量去用

函数名和变量名一样,都是指向的是内存地址

2. 函数名的用途

2.1 可以赋值

把一个函数名赋值给另一个变量名

# func=函数的内存地址
def func():
    pass

f=func        
print(func)   # <function func at 0x000001E78F9B3EA0> 
print(f)      # <function func at 0x000001E78F9B3EA0>

2.2 当参数

函数名也可以当做参数传给另一个函数

def func():
    pass

def foo(×):  #× = func的内存地址#
    print(x)
    x()

foo(func)  #foo( func的内存地址)

2.3 当返回值

函数名也可以当做函数的返回值

def func():
    pass

def foo(x):
    return x    


res = foo(func)
print(res)

2.4 当参数

函数名可以当做容器类型的一个元素

def func():
    print('func函数: ',func)
    
l=[func,]
print(l)
l[0]()        # 调用func函数
dic={ 'k1' :func}
print(dic)
dic['k1']()  # 调用func函数

2.5 小练习

def login():
    print('登录功能')


def transfer():
    print('转账功能')


def check_balance():
    print('查询余额')


def change_password():
    print('修改密码')


# 巧用数据类型
dic = {
    # '1': login,
    '0': ('退出', None),
    '1': ['登录', login],
    '2': ['转账', transfer],
    '3': ['查询余额', check_balance],
    '4': ['更改密码', change_password],
    # '2': transfer,
    # '3': check_balance,
}

while 1:
    for key, value in dic.items():
        print('{} {}'.format(key, value[0]))
    choice = input('请输入编号:').strip()
    if not choice.isdigit():
        print('必须输入编号,憨批')
        continue
    if choice == '0':
        break
    if choice in dic:
        dic[choice][1]()
    else:
        print('你输入的命令不存在,憨批')

3. 函数的嵌套

3.1 函数的嵌套调用

在调用一个函数的过程中又调用其他的函数

def func1():
    print("调用了func1函数")
    
def func2():
    print("调用了func2函数")
    
def func3():
    print("调用了func3函数")
    
    
def func():
    func1()
    func2()
    func3()

应用场景 :

# 把各个小功能(小函数),封装到一个大功能(大函数)里面
def max2(x, y):
    return x if x > y else y


# 比较4个参数的大小
def max4(a, b, c, d):
    # 比较a,b得到res1
    res1 = max2(a, b)
    # 比较res1,c,得到res2
    res2 = max2(res1, c)
    # 比较res2,d ,得到res3
    res3 = max2(res2, d)
    return res3


print(max4(7838237, 9859, 949, 90))

3.2 函数的嵌套定义

在函数内定义其他的函数

def func():
    def func1():
        pass
# 举例:
# 求圆的周长和面积,但是只在圆内调用,在其他地方不用
from math import pi


def circle(radius, action=None):
    "radius:半径 action为0是求周长 action为1是求面积 "

    def get_perimeter(radius):
        return 2 * radius * pi

    def get_area(radius):
        return pi * radius ** 2

    # 求圆的周长
    if action == 0:
        return get_perimeter(radius)
    # 求圆的面积
    elif action == 1:
        return get_area(radius)

上面的应用场景比较少,函数的嵌套定义更多的用在闭包

# 函数的嵌套定义你可以认为把一堆函数丢到一个大容器(最外面的那个函数)中
# 同时在外部是无法访问这个函数的,只能在大容器中访问这些函数

4. 闭包函数

闭包函数也可以直接简称为闭包

# 闭包函数 = 名称空间与作用域 + 函数嵌套 + 函数对象
# 核心点: 名字的查找关系是以函数定义阶段为准

4.1 什么是闭包函数

# "闭"函数指的是该函数为内嵌函数         是定义在一个函数内的函数

def f1():
    def f2():   # f2是闭函数
        pass
# "包是在闭的基础上"
# "包"函数指的该函数包含对外层函数(第几层无所谓)作用域名字的引用(不是对全局作用域)

def f1():
    x = 111
    def f2():
        print(x)    # x就是包,f2从外层函数f1那里引用

闭包函数 : 名称空间与作用域 + 函数嵌套

def f1():
    x = 222

    def f2():
        print(x)

    f2()


x = 111


def foo():
    x = 333
    f1()


foo()    # 打印2222

闭包函数 : 名称空间与作用域 + 函数嵌套 + 函数对象

def f1():
    x = 33333333333333333333

    def f2():
        print('函数f2:', x)

    return f2   # 这样一搞就有了骚操作,在全局能够访问到f2的内存地址了,就打破了
				# 以前在外部无法访问局部的值了


f = f1()


# f是全局的,但是f的值是局部的
# 牛逼之处你在全局拿到了在局部作用域的名字
# 牛逼的实现了,在任意地方都能访问到局部的值了,打破了层级限制
# 拖家带口的返回,每次你调用里面的x都是在局部定义的那个x

4.2 为什么要有闭包

为什么要有闭包换句话说就是闭包的应用场景有哪些?

闭包实现了函数的另一种传参方式,虽然没有直接传参那种看上去更加简单,但是有的应用场景就是直接传参

解决不了,那么你不要忘记还有一种闭包传参的方式

# 先写x=3和函数f2的定义,然后用f1包起来
def f1():
    x = 3

    def f2():
        print(x)

    return f2

f = f1() #此时f指的就是f2的内存地址
# 你可以写f,那么你也可以写成f2
# f2 = f1()

# 有的人就说了 你写成x=3不是写死了吗,那么我们可以这样写
def f1(x):
    def f2():
        print(x)

    return f2

# f = f1(1)
# f = f1(2)
# f = f1(3)  # 这样就做到了 你传几,就是几

5. 两种传参方式

一种就是直接传 还有一种就是通过闭包传

学习闭包就是学习了另一种函数传参方式,(曲线救国),当你有一中需求是函数直接

传参解决不了的时候,不要忘记还有闭包这种这种传参方式,下面介绍的装饰器就是

你无法直接传参,只能通过闭包传参来解决

posted @ 2020-11-25 21:34  Mn猿  阅读(62)  评论(0编辑  收藏  举报