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