day11:函数核心:对象,嵌套,名称空间和作用域,闭包

今日内容:函数的核心
1、函数对象
2、函数的嵌套
3、名称空间与作用域
名称空间与作用域的关系是在函数定义阶段就确立的,与调用位置无关
4、闭包函数 = 函数对象+函数嵌套+名称空间与作用域
 
一、函数对象:
在python中,函数是第一类对象,第一等公民
本质函数可以当变量用
1、可以赋值
2、可以当做参数传给另一个函数
3、可以当做函数的返回值
 
4、可以当做容器类型的元素
例:购物车内容
def withdraw():
print('提款')
 
def tranfer():
print('转账')
 
def check_balance():
print('查询余额')
 
def deposit():
print('转账')
 
func_dic = {
"1" : ['提款',withdraw],
"2" : ['转账',tranfer],
"3" : ['查询余额',check_balance],
"4" : ['转账',deposit]
}
 
while True:
print('0 退出')
 
for k,v in func_dic.items():
print(k,v[0])
 
chioce = input('>>>>:').strip()
if chioce == "0":
break
 
if chioce in func_dic:
func_dic[chioce][1]()
else:
print('输入错误,重新输入!')
 
二、函数嵌套:
函数嵌套的调用
例:比较4个值,哪个是最大值
def max2(x,y):
if x > y:
return x
else:
return y
 
def max4(a,b,c,d):
res1 = max2(a,b)
res2 = max2(res1,c)
res3 = max2(res2,d)
return res3
print(max4(11,22,33,77))
 
嵌套调用的写程序的一种套路:
基于函数嵌套的思想,可以把一个大功能拆解成一个个小功能,然后再把每个小功能分步实现,每个小功能实现的代码可能也就几行,然后在大功能里一点一点去调用,最后完成这个大功能
 
函数嵌套的定义:在函数内定义其他函数
就是想让不常用或者不想共享的函数归类在一个函数内,相当于丢到一个容器里
def f1():
x = 10
def f2():
print('from f2')
print(x)
print(f2)
# print(x) 
# print(f2)
f1()
-------------------------》
10
<function f1.<locals>.f2 at 0x0000022B0BD53F70>
 
 
三、名称空间与作用域名称空间与作用域的关系是在函数定义阶段就确立的,与调用位置无关
无论在哪调用,都应该回到定义阶段,去确定名称空间的嵌套关系
 
名称空间(namespace):存放名字与对象映射/绑定关系的地方,是对栈区的划分
有了名称空间后,就可以在栈区中存放相同的名字
 
a、内置名称空间:第一个被加载的名称空间,伴随python解释器的启动/关闭而产生/回收
存放的名字:存放的python解释器内置的名字
存活周期:python解释器启动产生,关闭则销毁
 
b、全局名称空间:第二个被加载的名称空间,伴随python解释器的开始执行/执行完毕而产生/回收
存放的名字:顶级!只要不是函数内定义的,也不是内置的,剩下的都是全局名称空间的名字
存活周期:python文件执行则产生,执行完毕则销毁
 
c、局部名称空间:伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存 放于该名称空间中
存放的名字:在调用函数时,在运行函数体代码过程中产生的函数内的名字
存活周期:在调用函数时存活,调用完毕后销毁
 
名称空间的加载顺序:内置-->全局-->局部
销毁顺序:局部-->全局-->内置
重点!查找名字的顺序/优先级:是以定义阶段为准,与调用位置无关,当前所在位置一层一层向外查找,局部(函数内-->外层函数)-->全局-->内置
 
例1:
def func():
print(x)
x = 66
func() ----------------》66
 
 
例2:名称空间和“嵌套”的关系-----》强化:以定义阶段为准,与调用位置无关
x = 1
def func():
print(x)
 
def foo():
x =22
func()
foo() ---------------》1
 
例3:函数嵌套定义(强化:理解查找名字的顺序)
一层一层注释掉,一层一层向上查找名字
input = 111
def f1():
input = 222
def f2():
input = 333
print(input)
f2()
f1()---------------》333
 
input = 111
def f1():
def f2():
input = 333
print(input)
input = 222
f2()
f1()-------------》依旧是333
 
例4:函数嵌套定义(强化:以定义阶段为准,与调用位置无关)
x = 111
def func():
print(x)
x = 222
=================定义阶段语法没有问题======================
 
func()--------》UnboundLocalError: local variable 'x' referenced before assignment
=============但是在调用阶段,逻辑问题产生报错================
 
作用域:作用范围
1、全局作用域:内置名称空间和全局名称空间共同之处,伴随整个程序运行的始终
内置名称空间、全局名称空间
特点:全局存活,全局有效,被所有函数共享
例:
x = 111
def func():
print(x,id(x))
 
def foo():
print(x,id(x))
 
func()
foo() -----------》111 140734266217568
print(x,id(x))
 
2、局部作用域:局部空间的名字
特点:临时存活,局部有效(函数内有效)
def f(x=10):
def f1():
def f2():
print(x)
f2()
f1()
f()
 
名称空间的另一种说法:LEGB规则(针对函数嵌套)
L:local E:enclosing G:global B:built-in
那么,查找名称顺序 :Local -> Enclosing -> Global -> Built-in
 
名称空间与作用域的关系,
 
四、global与nonlocal
1、global(慎用)
如果在局部想要修改全局的名字对应的值(值是不可变类型),需要用global
例:
x = 111
def func():
global x----》声明x是全局的名字,不要再造新的名字了
x = 222 -----》不可变类型,是产生新值,不加global也会产生新名字
 
func()
print(x)--------》222
 
l = [111,222] ------》可变类型(便于理解,直接更改参数即可,无需global)
def foo():
global l
l=[33,44,55,66]
foo()
print(l)-------》[33, 44, 55, 66]
 
l = [111,222]------》可变类型
def foo():
l.append(333)
foo()
print(l)--------》[111, 222, 333]
 
2、nonlocal(了解):声明变量是来自外层函数(不会到全局,全局报错)
需求:在函数内级别L下,改E级别的参数
x = 111
def f1():
x = 222
def f2():
nonlocal x
x = 333
f2()
print(x)
f1()
-----------------》333
 
x = 111
def f1():
x = 222
 
def f2():
x = 333
 
def f3():
nonlocal x # x = 333
x = 444
 
f3()
print(x)
f2()
 
f1()
 
五、闭包函数:为函数传参数的一种方式
闭:指的是该函数 是定义在函数内的函数
包:指的是该函数 引用了一个外层函数作用域(E)的名字
简而言之:先闭再包
闭:把wrapper缩进到一个函数内,只能在那个函数空间内使用,与外部空间隔绝
包:wrapper要访问一个变量x=11,而这个变量不是在它的这层名称空间,是外部的,所以在外部定义一个函数outter,把x和wrapper都包在里面
wrappe当初是在全局名称空间,
def outter():
x = 11
def wrapper(): 就是wrapper想调用外部变量
x
return wrapper 返回wrapper 可以通过函数对象的方式把wrapper返回到全局
f = outter() -----------》函数对象方式在全局中的f调用了在局部内的wrapper
 
方案一:直接用参数传
 
方案二:闭包函数
def outter(x):
def wrapper():
print(x)
return wrapper
 
f = outter()
print(f)
----------------------------》
<function outter.<locals>.wrapper at 0x0000022EB98F3F70>
 
精妙之处在于,f是全局名称空间的变量,但是f的值是来自函数内的
其实,基于函数对象的思想,可以把一个内部函数扔到全局,打破了层级限制
可以在任意位置调用
但是无论在哪调用,作用域关系以定义时为准
def outter():
x = 111
def wrapper():
print(x)
return wrapper------》千万别加括号(加括号等于扔了wrapper的返回值)
f = outter()
# print(f)
def foo():
x = 222
f()
foo()-----------》111,以定义为准
 
为函数体代码传参的方案:
方案一:直接用参数传
def wrapper(x):
print(x)
 
wrapper(111)
wrapper(222)
 
方案二:闭包函数
def outter(x):
# x = 111
def wrapper():
print(x)
return wrapper
 
f1 = outter(222)
f1()
f2 = outter(333)
f2()
这等于是将wrapper扔进outter,又扔出来return
把outter比作一个麻袋,wrapper比作一条狗,参数x = 111比作一根骨头
把狗wrapper装进麻袋outter,给了骨头x = 111,再被return扔出来,狗wrapper嘴巴扛着骨头111

posted @ 2020-12-30 15:30  Seven_1130  阅读(107)  评论(0)    收藏  举报