今日内容
-
命名关键字补充
-
函数对象
-
函数的嵌套和使用
-
名称空间
-
名称空间的查找顺序
-
作用域
一、命名关键字(了解)
一般情况下,按照形参的定义,默认参数需要放在位置参数之后,当将默认参数和位置参数定义在*和**可变长参数之后,无需将默认参数放在位置参数之后,是可以正常定义的。在定义阶段感觉好像是默认参数和位置参数,实质上叫做命名关键字参数。
给命名关键字传参时需要按照关键字传参。
def func(x,y=1,*args,z=3,m,**kwargs): print(x,y) # 1 2 print(args) # (1, 2, 3, 4, 5, 6, 7, 78, 8, 9, 0) print(z,m) # 69 999 print(kwargs) # {'o': 666999, 'l': 999666} func(1,2,1,2,3,4,5,6,7,78,8,9,0,z=69,m=999,o=666999,l = 999666)
二、函数对象
函数对象指的是函数是第一类对象,表现在函数名所指向的值可以被当作参数传递,也就是函数体代码的内存地址。具体传递函数名所指向的值的用法有四种:
1、函数名当作变量值传递个变量名
函数名所指向的值为函数体代码的内存地址,将函数名当作变量值传给某个变量名的时候,就相当于给变量名赋值的过程。
name = 'jason' x = name print(x) # jason print(id("jason")) # 2357108724152 print(id(x)) # 2357108724152 def func(): print('from func') print(func) # <function func at 0x00000224CE9B2E18> f = func func() # from func print(f) # <function func at 0x00000224CE9B2E18> f() # from func f其实指向的也是函数func指向函数体代码的内存地址,f后加上()即可调用源函数
2、函数名当作变量值传递给其它函数
函数接收变量名的时候,实际上接收到的是该函数名指向的函数体的内存地址,接收后可以进行变量名的赋值、打印、加括号当作函数使用等等。
def func(): print('from func') # from index def index(args): print(args) # <function func at 0x000001C3E2C22E18> args() # from func print('from index') index(func)
3、函数名也可以当作其它函数的返回值
def index(): print('index') def func(): print('func') return index res = func() # 调用函数,用res接收返回值,打印func print(res) # 打印res即func指向的函数体的内存地址,<function index at 0x000001FF0E342E18> res() # 执行index函数,打印index
4、函数名可以被当作容器类型的参数
def func(): print('func') print(func()) # None l = [1,2,func] print(l) # [1,2,<function func at 0x000001F7D79899D8>]
三、函数的嵌套定义与使用
一个函数可以执行特定的功能,当实现的功能复杂时,函数内部的代码会相对变复杂,此时将函数内部的代码分成多个函数执行,会简化内部复杂的逻辑关系,此时用到的方法就叫做函数的嵌套使用。
比如,已知比较两个数字大小的函数,此时的需求是比较4个数、6个数或者更多的数,如果重新写一个比较多个数的函数,会显得内部逻辑很复杂,此时如果在函数的内部取调用已知的比较两个数字的函数,将会大大缩减代码量。如下:
def my_max(x,y): # 比较两个数的大小 if x > y: return x return y def my_max4(a,b,c,d): # 比较四个数的大小 res1 = my_max(a,b) res2 = my_max(res1,c) res3 = my_max(res2,d) return res3 print(my_max4(1,2,10,4)) # 10
四、名称空间
1、什么叫名称空间?
名称空间指的是存放变量名与变量值的内存地址绑定关系的地方。要想访问一个变量的值,必须先去名称空间中拿到对应的名字,才能访问变量的值。
2、名称空间的分类
名称空间可以分为内置空间、全局命名空间以及局部命名空间。
1、内置空间:python解释器内部提前定义好的名字,解释器启动就已经存放到名称空间中去了,比如常用的print、len等。
2、全局命名空间:直接写在文件中,而不是写在函数或类中,当函数名不存在类中且是最外层函数的函数名时,此时的函数名也放在全局命名空间中。如下:
a = 1 if a == 1: b = 2 print(b) while True: z = 3 def fun(): pass
上面出现的a、b、z、fun都属于全局命名空间
3、局部命名空间
函数内部的变量名和函数名以及日后学习的类中的变量,都属于局部变量命名空间。
4、名称空间的生命周期
每个名称空间的生命周期是不一样的。
内置名称空间:只要python解释器已启动立马创建,关闭python解释器的时候内置名称空间自动销毁
全局名称空间:只要你右键运行.py文件会自动创建,.py文件程序运行结束自动销毁。
局部名称空间:函数被调用的时候自动创建,函数指向结束立即销毁,也叫动态创建动态销毁。
5、名称空间中的名字的查找顺序
判断一个名字去哪查找,首先确定在哪个空间开始查找,即当前所在的位置。
1、站在全局命名空间的时候,先在全局命名空间中查找,若没有找到则去内置命名空间查找,若内置命名空间没有,则会报错,没有定义改变量名。
2、站在局部命名空间的时候,先在局部命名空间中查找,若没有再去全局命名空间中查找,若还没有找到则去内置命名空间中红查找,若还是没有,则会报错,没有定义该变量名。
PS:实际上在函数定义的阶段查找名字的顺序就已经固定了
x = 111 def f1(): x = 222 def f2(): x = 333 def f3(): # x = 444 def f4(): print(x) f4() x = 777 # 纯粹为了教学演示 f3() f2() f1()
x = 111 def f1(): x = 222 def f2(): x = 333 def f3(): x = 444 def f4(): print(x) x = 777 # 纯粹为了教学演示 f4() f3() f2() f1()
五、作用域
1、什么是作用域?
每个变量名都有它的有效使用范围,该范围叫做作用域。
2、
作用域可以分成全局作用域和局部作用域。
内置命名空间和全局命名空间中的变量名的作用范围叫做全局作用域;局部命名空间中的变量名的作用范围叫做局部作用域。
理论在局部作用域是不可以修改全局作用域相同变量名的值,局部作用域间的相同的变量的值也是不可以修改的,但可以通过关键字global和nonlocal来实现相应的功能。
# global x = 1 # 不可变类型 username = 'jason' def func(): # x.append('嘿嘿嘿') global x,username # 修改全局变量 而不是创建局部名称空间 x = 999 username = 'egon' func() print(x) # 999 print(username) # egon x = 1 # 不可变类型 username = 'jason' def func(): x = 999 username = 'egon' func() print(x) # 1 print(username) # jason、 # 不加global,函数fun中的变量不影响全局变量
# nonlocal def func(): x = 1 def index(): nonlocal x x = 2 index() print(x) func() # 2 # 不加nonlocal,函数index中的变量不影响函数func中的变量的值 def func(): x = 1 def index(): x = 2 index() print(x) func() # 1