1.函数的对象
精髓:可以把函数当成变量去用
1.1 可以赋值
# func=内存地址
def func():
print('from func')
f=func
print(f,func)
f()
输出:
<function func at 0x0000017A4E24F7F0> <function func at 0x0000017A4E24F7F0>
from func
1.2 可以把函数当做参数传给另外一个函数
'''示例1'''
def func():
print('from func')
def foo(x): # x = func的内存地址
print(x)
# x()
foo(func) # foo(func的内存地址)
输出:<function func at 0x0000028FE716F7F0>
'''示例2'''
def func():
print('from func')
def foo(x):# x=func的内存地址
# print(x)
x() # 调用func函数
foo(func) # foo(func的内存地址)
输出:from func
1.3 可以把函数当做另外一个函数的返回值
def foo(x): # x=func的内存地址
return x # return func的内存地址
res=foo(func) # foo(func的内存地址)
print(res) # res=func的内存地址
res()
输出:
<function func at 0x0000011745EDF7F0>
from func
1.4 可以当做容器类型的一个元素
如:列表、字典、元组
def func():
print('from func')
''''''
l=[func,]
print(l) # 列表l的内存地址 输出:[<function func at 0x7f821a3dd1e0>]
l[0]() # l[0]的结果为名字func的内存地址,指向函数func,l[0]加()结果为func()调用func函数
dic={'k1':func}
print(dic) # 字典dic的输出:{'k1': <function func at 0x7fcd4bbaa1e0>}
dic['k1']() # dic['k1']获取到func的内存地址,加()表示调用func函数
tup = (11, func)
print(tup[1])
tup[1]()
输出:
[<function func at 0x7efdfd9df1e0>]
from func
{‘k1’: <function func at 0x7efdfd9df1e0>}
from func
<function func at 0x7fe9718651e0>
from func
1.5 经典案例:函数对象应用示范
案例要求:写ATM机的基本业务功能,包括登录、转账、查询余额、提现、退出等功能
1.5.1 初级写法
'''###案例1:初级写法'''
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_balance():
print('查询余额')
def withdraw():
print('提现功能')
while True:
print('''
0 退出
1 登录
2 转账
3 查询余额
4 提现
''')
choice = input('请输入需要使用的功能编号:').strip()
if not choice.isdigit():
print('请输入范围内的功能编号!!!')
continue
if choice == '0':
print('已退出')
break
elif choice == '1':
login()
elif choice == '2':
transfer()
elif choice == '3':
check_balance()
elif choice == '4':
withdraw()
else:
print('输入的指令不存在,请重新输入')
点评:上述代码的可扩展性极差,如果需求变更,要求新增一个“注册”功能,我们需要修改三处,一是定义一个新的函数,功能为注册;二是while函数体类的用户信息需要新增;三是源代码中需要添加elif choice=5,再调用一下“注册”功能的函数;如果要增加10个功能,维护起来及其麻烦。我们来优化一下~~
1.5.2 中级写法
简化方向:见标题“1.4”,可以把函数当作容器类型的一个元素,将函数整合到字典里
'''###案例2:中级写法'''
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_balance():
print('查询余额')
def withdraw():
print('提现功能')
func_dic = {
'1':login,
'2':transfer,
'3':check_balance,
'4':withdraw
}
# func_dic['1']()
# func_dic['1']值为login的内存地址,加()表示调用login()函数功能
while True:
print('''
0 退出
1 登录
2 转账
3 查询余额
4 提现
''')
choice = input('请输入需要使用的功能编号:').strip()
if not choice.isdigit():
print('请输入范围内的功能编号!!!')
continue
if choice == '0':
print('已退出')
break
if choice in func_dic:
func_dic[choice]()
else:
print('输入的指令不存在,请重新输入')
点评:当需要新增功能时,上述代码也需要修改函数体,当函数体代码量过大时容易引入bug,思考下如何保证函数体内的函数不动,只修改函数体外的代码。
1.5.2 高级写法
简化方向:提高函数体的扩展性,将功能说明信息和功能整合到字典,把功能信息和函数功能当作容器的一个元素
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现')
def register():
print('注册')
func_dic={
'0':['退出'],
'1': ['登录功能', login],
'2': ['转账功能', transfer],
'3': ['查询余额', check_banlance],
'4': ['提现', withdraw],
'5': ['注册', register],
}
while True:
for k in func_dic:
print(k, func_dic[k][0])
choice = input('请输入命令编号:').strip()
if not choice.isdigit():
print('请输入范围内的功能编号,编号,编号!!!')
continue
if choice == '0':
break
if choice in func_dic:
func_dic[choice][1]()
else:
print('输入的指令不存在')
2. 函数嵌套
2.1 函数的嵌套调用:
说明:在调用一个函数的过程中又调用其他函数
案例:比较一串数字的大小
思路分析:
1.第一个和第二个数字先比较大小,留下大的那一个;
2.第一步中最大的数与第三个数比较,留下大的那一个;
3.第二步中最大的数与第四个数比较,留下大的那一个;
…
…
所以 将两两比较大小的功能封装成函数
'''案例:筛选出一串数字中最大的那个'''
def max2(x,y):
if x > y:
return x
else:
return y
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
res=max4(1,2,3,4)
print(res)
2.2 函数的嵌套定义
说明:在函数内定义其他函数
def f1():
def f2():
pass
''' 求圆形的周长:2*pi*radius
求圆形的面积:pi*(radius**2)
'''
def circle(radius,action=0):
from math import pi
def perimiter(radius):
return 2*pi*radius
# 求圆形的求面积:pi*(radius**2)
def area(radius):
return pi*(radius**2)
if action == 0:
return 2*pi*radius # 返回函数式
elif action == 1:
return area(radius) # 返回函数
# 调用方法1
result = circle(2,action=0)
print(result)
# 调用方法2
result = circle(2,action=0)
print(result)
3. 闭包函数
3.1 前期提要
闭包函数 = 名称空间与作用域+函数嵌套+函数对象
核心点:名字的查找关系是以函数定义阶段为准
3.2 什么是闭包函数
“闭”函数:指的该函数是内嵌函数
“包”函数:指的该函数包含对外层函数作用域名字的引用(不是对全局作用域)
3.1.1 案例1:闭包函数之名称空间与作用域的应用
''' 案例1 闭包函数:名称空间与作用域的应用'''
def f1():
x = 33333333333333333333
def f2():
print(x)
f2()
f1() # 输出:33333333333333333333
补充说明:也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)
3.1.2 案例2:闭包函数之名称空间与作用域的应用+函数嵌套
''' 案例2 闭包函数:名称空间与作用域的应用+函数嵌套'''
def f1():
x = 33333333333333333333
def f2():
print(x)
f2()
x=11111
def bar():
x=444444
f1()
def foo():
x=2222
bar()
foo() # 输出:33333333333333333333
3.3 闭包函数:函数对象
def f1():
x = 33333333333333333333
def f2():
print('函数f2:',x)
return f2
f=f1() # 返回函数f2的内存地址
print(f) # 输出:<function f1.<locals>.f2 at 0x7ff3a9819d08>
x=4444
f() # 输出:函数f2: 33333333333333333333
# 解释:1、f存的是f1(),f1()返回值为函数f2的内存地址,故f()相当于调用了函数f2
# 2、f2是f1的内嵌函数,即函数f2为“闭”函数,f2包含对外层函数作用域名字的引用
def foo():
x=5555
f()
foo() # 相当于调用f()==>f2(),故输出为:函数f2: 33333333333333333333
3.4 闭包函数的应用
为何要有闭包函数,两种为函数体传参的方式
3.4.1 方式一:直接把函数体需要的参数定义成形参
'''案例1 改革开放前:直接把函数体需要的参数定义成形参'''
def f2(x):
print(x)
f2(1)
f2(2)
f2(3)
3.4.2 方式二:将需要传入的值包给函数
''' 案例2 改革开放后:'''
def f1(x):
def f2():
print(x)
return f2
x=f1(3)
print(x) # 返回的是函数f2的内存地址
x() # 调用f2,即打印结果为 3
3.4.3 更加直观的案例
'''传参的方案一:把函数需要的值定义为形参'''
import requests
def get(url):
response=requests.get(url)
print(len(response.text))
get('https://www.baidu.com')
get('https://www.cnblogs.com/linhaifeng')
get('https://zhuanlan.zhihu.com/p/109056932')
'''传参的方案二:把函数需要的值包给外层函数'''
import requests
def outter(url):
# url='https://www.baidu.com' # 将url传到outter,相当于在outter的作用域内定义了 url='https://www.baidu.com'这个变量
def get():
response=requests.get(url)
print(len(response.text))
return get
baidu=outter('https://www.baidu.com')
baidu()
baidu()
baidu()
<分析>:对比两种方式,方案一在下载同一页面时需要重复传入url,而方案二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url.