06-04 函数对象+函数嵌套+闭包

一、函数对象的4种用法介绍

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

1、函数可以被引用(也可以说可以被赋值)

def func():
    print('from func')

f = func
f()  # from func

2、函数可以作为容器类型的元素

def func():
    print('from func')

li = [func]
print(li)  # [<function func at 0x000001D99DC2CEE0>]
li[0]()  # from func

3、函数可以作为参数传入另一个函数

def func():
    print('from func')

def foo(x):
    print(x)  # <function func at 0x000001BA30E2CEE0>
    x()

foo(func)  # from func

4、函数当做另外一个函数的返回值

def func():
    print('from func')

def foo(x):
    return x

res = foo(func)
print(res)  # <function func at 0x000001618F52CEE0>
res()  # from func

5、函数对象应用示范

# 模拟用户登录,注册。登录以后可以实现转账、查询余额、提醒、充值功能
def login():
    print("登录功能")

def register():
    print("注册功能")

def transfer_accounts():
    print("转账功能")

def check_banlance():
    print("查询功能")

def withdraw_deposit():
    print('提现')

def recharge():
    print('充值')

func_dic = {
    '0': ('退出', exit),
    '1': ('登录', login),
    '2': ('注册', register),
    '3': ('查询余额', check_banlance),
    '4': ('提线', withdraw_deposit),
    '5': ('充值', recharge),
    '6': ('转账', transfer_accounts),
}

while True:
    for key, value in func_dic.items():
        print(f'{key}:{value[0]}')
    cmd = input("输入命令编号>>:").strip()
    if not cmd.isdigit():
        print("请输入命令对应的数字编号")
        continue
    if cmd not in func_dic:
        print("输入命令编号范围超出")
        continue
    func_dic[cmd][1]()

二、函数嵌套

  • 拓展:导入Π的值:from math import pi

1.函数的嵌套调用:在调用一个函数的过程中又调用其他函数组合

# 使用场景:把大功能拆分成一个一个小功能
def max2(a, b):
    if a > b:
        return a
    elif a < b:
        return b
def maxs(a, b, c, d):
    res = max2(a, b)
    res = max2(res, c)
    return max2(res, d)
print(maxs(1, 2, 3, 4))  # 4

2.函数的嵌套定义:在函数内定义其他函数

# 使用场景: 小功能归类组合成大功能
# 需求:求圆的周长和面积
# 周长(perimeter) = 2 * pi * 半径(radius)
# 面积(area) = pi * 半径 ** 2
def circle(radius, action=0):
    from math import pi
    def perimeter(radius):
        return 2 * pi * radius
    def area(radius):
        return pi * radius ** 2
    if action == 0:
        res = perimeter(radius)
    elif action == 1:
        res = area(radius)
    return res
print(circle(3))  # 求圆的周长:18.84955592153876
print(circle(3, 1))  # 求圆的面积:28.274333882308138

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

1、大前提

  • 闭包函数是对名称空间与作用域, 函数对象, 函数嵌套的综合运用

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

  • 核心点:名字的查找关系是以函数定义阶段为准的

2、什么是闭包函数?闭 & 包

  • '闭':指的是该函数是内嵌函数。
  • '包':指得是该函数对外层函数(只要是外层函数就行)作用域名字的引用(不是对全局作用域)
"""
注意:
	闭包函数打破了名称空间之间的访问的层级限制,让全局变量可以访问到函数的局部名称空间中的函数的内存地址.
闭包函数的精妙之处:
	无论怎么调用,都是用它自带的那个值。
"""
# 闭包函数: 名称空间与作用域的应用 + 函数嵌套
def f1():
    x = 333333333333
    def f2():
        print(x)  # 333333333333
    f2()
    
x = 111111111
def bar():
    x = 444444444
    f1()
    
def foo():
    x = 222222222
    bar()
foo()
"""
全局: f1, x, bar, foo
foo局部: x
bar局部: x
f1局部: x, f2
f2局部: 
"""

# 闭包函数: 函数对象
def f1():
    x = 3333333333333
    def f2():
        print(x)  # 3333333333333
    return f2

f = f1()
print(f)

x = 44444444444
def foo():
    x = 55555555555
    f()
foo()
"""
全局: f1, f, x, foo
foo局部: x, f
f1局部: x, f2
f2局部: 
"""

3、闭包函数的应用

# 两种给函数体传参的方式
# 方式一: 直接把函数提需要的参数定义成形参
def f2(x):
    print(x)

f2(1)
f2(2)
f2(3)

# 方式二: 使用内嵌函数把函数包裹, 为被包裹的函数提供作用域名字的引用
def f1(x):
    # x = 333
    def f2():
        print(x)
    return f2

f = f1(333)  # 把原本属于f1局部的函数的名字, 拿到了全局.
print(f)  # <function f1.<locals>.f2 at 0x000001DBF8C3CE50>

f()  # 333, 实现了多次调用同一种传参的功能, 不需要重复传参.
f()  # 333
f()  # 333
  • 实例演示
import requests

'''
requests属于第三方模块, 需要下载. 在设置了pip环境变量的情况下,直接使用命令: pip3 install requests
'''

# 1. 传参的方案一: 利用函数封装思想, 实现多次调用, 多次传不同参数的不同结果
def get(url):
    response = requests.get(url)
    print(len(response.text))

# 这个时候我们发现我们每次如果都想操作同一个地址, 那么需要重复传3次, 造成的代码的冗余. 这个时候我们就是用方案二.
get("https://www.cnblogs.com/yang1333/p/12494699.html")  # 17028
get("https://www.cnblogs.com/yang1333/p/12494699.html")  # 17028
get("https://www.cnblogs.com/yang1333/p/12494699.html")  # 17028

# 如果我们只需要操作同一个地址一次,这个时候我们就用方案一.
get("https://www.bilibili.com/video/av97157528/")  # 56845
get("https://www.cnblogs.com/yang1333/p/11807531.html")  # 308036


# 2. 传参的方案二: 利用闭包函数实现只需要给一次参数,就能多次不传参达到调用相同功能的目的
def outer(url):
    def get():
        response = requests.get(url)
        print(len(response.text))
    return get

my_python_cnblogs = outer("https://www.cnblogs.com/yang1333/p/12494699.html")
my_python_cnblogs()  #17028
my_python_cnblogs()  # 17028
my_python_cnblogs()  # 17028
posted @ 2020-03-20 18:04  给你加马桶唱疏通  阅读(135)  评论(0编辑  收藏  举报