
名称空间
什么是名称空间
namespaces名称空间:用于存放变量名与变量值绑定关系的地方(类似于民政局)
名称空间的分类
内置名称空间:存放内置的名字(python解释器提前定义好的 )
- 生命周期:python解释器启动则产生,关闭则销毁
全局名称空间:在py文件中顶格编写的代码运行之后都会存入全局名称空间
- 生命周期:运行python文件时则产生,python文件运行完毕则销毁
局部名称空间:函数体代码运行之后产生的都是局部名称空间
- 生命周期:调用函数则产生,函数调用完毕则销毁
# 内置名称空间:input<built-in function input> # built-in:内置# 全局名称空间:x = 10 # 变量名Xy = 20 # 变量名yf 1 > 0:z = 30with open('a.txt', mode='wt') as f:a = 333while True:c = 444# 以上都是属于顶格的# 局部名称空间:x = 10 # 全局名称空间def foo(m): # 全局名称空间m = 111 # 调用函数时,才会执行函数代码,名字m和n都存放于该函数的局部名称空间中n = 222 # 函数内foo(111) # 调用产生函数名称空间
名称空间的加载顺序:
内置名称空间—->全局名称空间—->局部名称空间
名称空间的查找顺序:
查找一个名字,必须从三个名称空间之一找到,要先确定自己当前在哪,基于当前所在的位置向外查找
如果当前在局部查找,查找顺序为:
局部名称空间>>>>全局名称空间>>>>内置名称空间
如果当前在全局查找,查找顺序为:
全局名称空间>>>>内置名称空间
len = 10 # 全局名称空间def func(): # 函数len = 20# print(len) # 站在局部查找,找到的是20func() # 调用产生局部名称空间print(len) # 站在全局找,全局有找全局,全局没有找内置# 局部名称空间的嵌套'''函数在定义阶段名字的查找顺序就已经固定死了(特例)'''x = 111def f1():x = 222def f2():x = 333def f3():x = 444def f4():x = 555print(x)# x = 555 特例f4()f3()f2()# 通过调用f1 执行所有的函数f1()

作用域
作用域:名称空间所能够作用的范围
- 全局作用域:内置名称空间 or 全局名称空间
- 特点:全局存活(除非被删除,否则在整个文件执行过程中存活)
- 程序任何阶段任何位置均可使用(全局有效
- 特点:全局存活(除非被删除,否则在整个文件执行过程中存活)
- 局部作用域:局部名称空间
- 特点:临时存活(即在函数调用时临时生成,函数调用结束后就释放)
- 一般情况下只在各自的局部名称空间中有效(局部有效)
- 特点:临时存活(即在函数调用时临时生成,函数调用结束后就释放)
名称空间与作用域的关系是在函数定义阶段就确立的,与什么时候调用以及调用位置无关
# 例1x = 111def f1():print(x) # 在定义阶段就已经确定了要去全局找def f2():x = 222f1()f2() # 111# 例2x = 111def f1():print(x)x = 222f1() # 报错"""名称空间与作用域的关系是在函数定义阶段扫描语法的时候就确立的"""
global关键字
在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字
# 例1:l = [] # 全局变量可变类型def func():l.append(1111) # 局部改全局func() # 调func执行appendprint(l) # [1111]"""当全局变量是可变类型的时候,局部是可以直接改的要想在局部修改一个全局的不可变类型,可以用global修改"""# 例2:x = 111 # 全局变量不可变类型def func():global x # 声明x是属于全局的,是可以改变的x = 222func()print(x) # 222
nonlocal关键字
对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外层嵌套函数定义的作用域(非全局)
x = 111def f1():# x = 222def f2():nonlocal x # 声明变量名是来自于外层函数的,不是全局x = 333 # 想在这个位置把x=222改为x=333,就要用nonlocalf2()print(x)f1()"""如果想要在内部的局部修改外部局部的不可变类型数据需要关键字nonlocal声明"""

函数对象
在python中,函数是第一类对象,即函数可以当做数据传递,函数又称第一等公民
本质:函数可以当变量用
1.函数名可以当做变量名赋值
def func():print('from func')a = func # 可以赋值给其它变量print(a)a() # 本质就是在调用func函数'''<function func at 0x7ff6b61b91f0>from func'''
2.函数名可以当做函数的实参
def func():print('from func')def foo(x):print(x)x() # 可以加()触发内部代码的运行foo(func)'''<function func at 0x7fbe691b91f0>from func'''
3.函数名可以当做函数的返回值
def func():print('from func')def foo(x): # 把func丢进去return x # 紧接着又扔出来funcres = foo(func)print(res) # <function func at 0x7fc9290b91f0>
4.函数名可以当做容器类型(内部可以存放多个数据)的元素
def func():print('from func')l = [func,] # 列表调用print(l)l[0]()'''[<function func at 0x7f7ae29b91f0>]from func'''# 容器类型的元素的应用:购物车程序def register():print('注册功能')def login():print('登录功能')def shopping():print('购物功能')def transfer():print('转账功能')def withdraw():print('提现功能')def check_order():print('查看订单')func_dic = {'1':register,'2':login,'3':shopping,'4':transfer,'5':withdraw,'6':check_order}while True:print("""1.注册功能2.登录功能3.购物功能4.转账功能5.提现功能6.查看订单""")choice = input('请输入功能编号>>>:').strip()# 判断用户输入的编号在不在字典的k中if choice in func_dic:# 根据键获取值(函数名)func_name = func_dic.get(choice)# 函数名加括号调用func_name()else:print('功能编号不存在')

函数嵌套
函数的嵌套调用
# 嵌套调用:函数内部调用其他函数def max2(x, y): # 比较两个值的大小if x > y:return xelse:return ydef max4(a, b, c, d): # 求四个值的最大值res1 = max2(a, b)res2 = max2(res1, c)res3 = max2(res2, d)return res3print(max4(11, 99, 33, 12)) # 99'''把一个大功能拆解成几个小功能,然后再把每个小功能分别去实现,最后在拼接回来'''
函数的嵌套定义
# 函数体内部定义其他函数# 将复杂的功能全部隐藏起来,暴露一个简单的接口def f1():x = 10 # 变量丢到函数里面是一个封闭的效果def f2():print('from f2')print(x) # 这行代码是站在内部去看的,可以看见print(f2) # 这行代码也是站在内部去看的,可以看见f1()# print(x) # 报错,这行代码是站在外面去看的,是看不见的# print(f2) # 报错,这行代码是站在外面去看的,是看不见的'''10<function f1.<locals>.f2 at 0x7f8d84908550>'''# 函数层级带来的变量访问的限制

闭包函数
闭:定义在函数内部的函数
包:内部函数使用了外部函数名称空间中的名字
只有符合上述两个特征的函数才可以称之为”闭包函数”
def outer():x = 111 # 为index函数的函数体代码传参def index():print(x)# 以后无论index在哪调,它访问的x以定义阶段为准。# 示例def outer(): # outer最外层函数x = 111def wrapper(): # 把wrapper闭到了outter里面了,wrapper就只能在里面用print(x)return wrapper # 千万别加括号,要想把wrapper从里面扔出来用就需要用returnf = outer() # 把一个内部函数扔到全局,这个内部函数就打破了层级限制,可以在任意位置调用print(f) # <function outer.<locals>.wrapper at 0x7fbedc908550>def foo():x = 222f() # 此时调f就相当于调wrapperfoo() # 111
为函数代码体传参的方案
方案一:函数体代码需要用到数据,直接在括号内定义形参即可
def wrapper(x):print(x)wrapper(111)wrapper(222)wrapper(333)
方案二:利用闭包函数
def outter(x): # 为了给wrapper函数传参只能把它扔进去,wrapper本来是顶级的def wrapper():print(x)return wrapper # 参数传完就把wrapper用return扔出去f1 = outter(111) # 再想拿到wrapper,调outter拿到返回值,f1相当于当初的wrapperf1() # 111f2 = outter(222)f2() # 222
