一 函数对象
1.1 赋值给变量
1.2 函数可以作为容器类型的元素
1.3 函数可以作为参数传入另外一个函数
1.4 函数的返回值可以是一个函数
**案例** **利用该特性,优雅的取代多分支的if**
二 闭包函数
2.1 闭与包 :无论在哪里调 都是在函数定义阶段,找作用域关系
闭 函数指的是内层函数是定义在一个函数内的函数
包 函数指的是内层函数包含对外层函数作用域名字的引用(不是对全局作用域)
2.2 两种为函数体传参的两种方式:1.直接传参 2. 闭包
一 函数对象
函数对象指的是函数可以被当做’数据’来处理,具体可以分为四个方面的使用,我们如下
1.1 赋值给变量
>>> def add(x,y):
... return x+y
...
>>> func=add
>>> func(1,2)
3
1.2 函数可以作为容器类型的元素
>>> dic={'add':add,'max':max}
>>> dic
{'add': <function add at 0x100661e18>, 'max': <built-in function max>}
>>> dic['add'](1,2)
3
1.3 函数可以作为参数传入另外一个函数
>>> def foo(x,y,func):
... return func(x,y)
...
>>> foo(1,2,add)
3
1.4 函数的返回值可以是一个函数
def bar():
return add
func=bar()
func(1,2)
3
案例 利用该特性,优雅的取代多分支的if
# 应用场景
def login():
print("登录成功")
def transfer():
print("转账成功")
def check_banlance():
print("查询余额")
def withdraw():
print("提现")
while True:
print("""
0 退出
1 登录
2 转账
3 查询余额
""")
choice = input("请输入编号").strip()
if not choice.isdigit():
print("请输入编号")
continue
if choice == "0":
break
if choice == "1":
login()
elif choice == "2":
transfer()
elif choice == "3":
check_banlance()
else:
print("输入的指令不存在")
# 痛点
# 假如以后我要增加10个功能,那么我还得写10个elif。程序员不能这么干。
# 如果我们把函数名也就是函数内存地址,存在一个容器里面,是否可以实现呢?比如字典 列表
# 字典可以通过key去取函数内存地址,列表可以for循环 forkey,func in enumerate(list)
# 怎么解决: 把函数名作为一个字典的值
def login():
print("登录成功")
def transfer():
print("转账成功")
def check_banlance():
print("查询余额")
def withdraw():
print("提现")
def recharge():
print("充值")
# 之后 新增的功能只要加到字典里就行,判断输入key在不在func_dict里面,在就通过key取出来加括号调用
func_dict={
"1":login,
"2":transfer,
"3":check_banlance,
"4":withdraw,
"5":recharge
}
while True:
print("""
0 退出
1 登录
2 转账
3 查询余额
4 提现
5 充值
""")
choice = input("请输入编号").strip()
if not choice.isdigit():
print("请输入编号")
continue
if choice == '0':
break
if choice in func_dict:
func_dict[choice]()
else:
print("输入的指令不存在")
继续优化提示信息
# 继续优化 提示信息
def login():
print("登录成功")
def transfer():
print("转账成功")
def check_banlance():
print("查询余额")
def withdraw():
print("提现")
def recharge():
print("充值")
# 字典的value用元组存函数名 和提示信息
func_dict={
"0":("退出",None),
"1":("登录",login),
"2":("转账",transfer),
"3":("查询余额",check_banlance),
"4":("提现",withdraw),
"5":("充值",recharge)
}
while True:
# 前面提示信息这里用for循环打印
for k,msg in func_dict.items():
print(k,msg[0])
choice = input("请输入编号-->:").strip()
if not choice.isdigit():
print("请输入编号")
continue
if choice == "0":
break
if choice in func_dict:
func_dict[choice][1]()
else:
print("输入的指令不存在")
二 闭包函数
2.1 闭与包
# 闭包函数= 名称空间与作用域+函数嵌套+函数对象
# 核心点:名字的查找关系是以函数定义阶段为准
def f1(): # f1相当于麻袋
x = 1
def f2(): # f2闭函数,要去引用外层作用域才是包函数
print("函数f2:",x)
return f2
f = f1() # 拿到f,无论在哪里调都是用它外层的x.一个局部的f2可以在全局f去调用f2函数
x = 6666
f() # 无论在哪里调 都是在函数定义阶段,找作用域关系。 # 1
def foo():
x = 555 # 下面一行代码调用,不会取555,而是在函数定义处去找。x=1
f()
# 什么是闭包函数
# 闭 函数指的是内层函数[f2]是定义在[f1]一个函数内的函数
# 包 函数指的是内层函数[f2]包含对外层函数[f1]作用域名字的引用(不是对全局作用域)
基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。
x=1
def f1():
def f2():
print(x)
return f2
def f3():
x=3
f2=f1() #调用f1()返回函数f2
f2() #需要按照函数定义时的作用关系去执行,与调用位置无关
f3() #结果为1
2.2 两种为函数体传参的两种方式
# 方式一 : 直接把函数体需要的参数定义成形参
def my(x):
print(x)
my(2)
my(3)
# 方式二:
def f1():
x = 3
def my():
print(x)
return my
m = f1() # 重新拿到 f1下my的内存地址,等于是my的内存地址指向m,m就是my
m()
def f1(x):
# x = 3 这里注释,那么可以给函数f1可以传一个参数 x
def my():
print(x)
return my
m = f1(3) # 重新拿到 f1下my的内存地址,等于是my的内存地址指向m,m就是my
m()
print(m.__closure__)
# (<cell at 0x00000000028C2588: int object at 0x000007FECF90E390>,)
print(m.__closure__[0].cell_contents) # 3
也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)
x=1
def outer():
x=2
def inner():
print(x)
return inner
func=outer()
func() # 结果为2
可以通过函数的closure属性,查看到闭包函数所包裹的外部变量
>>> func.__closure__
(<cell at 0x10212af78: int object at 0x10028cca0>,)
>>> func.__closure__[0].cell_contents
2
“闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。
总结: 闭包其实就是多一种为函数传参的方式
,下面给个案例
import requests
# 方式一: 直接传参
def get(url):
res = requests.get(url)
print(len(res.text))
# 方式2:闭包
def outter(url):
# url= "https://www.baidu.com"
def get(): # 闭包这里可以看成把 4--6的代码,用outter包起来。url作用域外层的传参
res = requests.get(url)
print(len(res.text))
return get #
#
g = outter("https://www.jianshu.com/u/d653f5c19c1b")
g()
对比两种方式,方式一需要重复传入url,而方式二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url
网友评论