9. 函数参数与名称空间
函数介绍
什么是函数
函数就相当于具备某一功能的工具
函数的使用必须遵循一个原则:
先定义,后调用
函数分为内置函数和自定义函数
使用函数所解决的问题
1、组织结构不清晰,可读性差
2、代码冗余
3、可维护性、扩展性差
函数的定义与调用
'''
def 函数名(参数1,参数2.....)
'''文档描述'''
函数体
return 函数返回值的值
'''
# 定义函数的方式一:无参函数
def func():
print('nihao')
# 方式二:有参函数
def func(a):
print(a)
# 方式三:空函数,用于构思代码时使用
def func(a):
pass
定义函数发生的事情:
- 申请内存空间保存函数的代码
- 将内存空间的地址绑定给函数名
- 定义函数并不会执行函数的代码,但是会检查函数体的语法,如果有问题不需要调用就会报错
# 调用函数的三种形式
# 方式一:语句的形式,只加括号执行
func()
# 方式二:表达式形式
res=function()
# 方式三:函数调用可以做参数
# print(add_two(1,2))
调用函数发生的事情:
- 通过函数名找到函数的内存地址
- 加上括号就是进行函数体代码的执行
函数的返回值
就是函数的结果,关键字是return
def add_two(a,b):
return a+b
return是函数终止的标志,并且会将return后面的值返回函数的结果
返回值的三种形式:
- 函数内没有return或者是指写一个return,函数返回none
- 返回一个值:return 结果
- 返回多个值:return a , b , c 函数会以元组的形式返回一个值
函数的参数
简介
形式参数(形参):在函数定义阶段定义的参数,形参相当于变量名
def func(x,y) # x,y就是形参
实际参数(实参):在调用函数阶段调用的值,实参相当于变量值
func(1,2) # 1,2就是实参(这里的1,2是值,就是说代表值的变量,返回值的表达式都可以作为参数)
python中传递的实质上是值的内存地址,而不是值本身
形参与实参的关系:在调用阶段,实参会绑定给形参,这种绑定关系只是在函数体内有效,在函数调用的时候,绑定关系生效,在调用结束后,绑定关系解除(都是在内存中产生的,调用结束后直接释放内存)
形参传参:
-
位置参数:按照从左到右位置一次定义的参数,位置形参必须被传值,必须一一对应
-
默认参数:以name=value的形式传入的参数,默认参数在函数调用时可以不进行传值,即使用默认值
-
动态参数(*和**的用法):传入的参数的个数不固定时,使用这种用法
# 1. 专门接收溢出的位置实参 # 溢出的位置实参将会被*保存成元组然后赋值给紧跟其后的变量名,一般使用*args表示(规范用法) # 2. 专门接收溢出的关键字实参 # 溢出的关键字实参将会被**保存成字典然后赋值给紧跟其后的变量名,一般使用**kwargs表示(规范用法) -
命名关键字参数:定义函数时,在*后面的参数
def func(x,y,*,a,b): # a和b就是命名关键字参数,在传参的时候,需要按照关键字参数的形式进行传值,因为在形参上是位置参数的形式,但是传参的时候必须以关键字参数传值的形式,所以叫命名关键字参数,有的也叫仅限关键字参数 pass形参设置的时候参数顺序的关系:
位置参数、默认参数、*args、命名关键字参数,**kwargs
形参混用的问题:
-
位置参数必须在默认参数的左边
-
默认参数的值是在函数定义是就被赋值的
-
虽然默认值参数可以指定任意类型,但是不建议使用可变的数据类型
def func(x,y,l=None): if l=None: l=[] l.append(x) l.aapend(y) -
混用时:*args必须在**kwargs之前
实参传参:
- 位置参数:与位置一一对应的进行传值,位置形参必须被传值
- 关键字参数:函数调用的时候,以name=value的形式传入的参数,关键字参数可以不参照顺序
- *的实参用法:实参中带星就是将后面的数据打散成位置参数,如func(*[1,2,3])就是func(1,2,3),一个效果
- **的实参用法:当**的后面跟的是一个字典的时候,会将字典打散成若干关键字参数func(**{1:'a',2:'b'})等同于func(1='a',2='b')
混合使用的时候,需要注意以下几点:
- 可以混合使用,位置实参必须放在关键字实参的前面
- 不能给同一个形参多次进行传值
实参设置的时候参数顺序的关系:
遇到*进行传参,先将后面的可迭代对象打散成位置参数,这样的话*用法传递的参数必须在关键字参数的后面了,这里位置参数必须在关键字参数的前面,剩下的都可以灵活调换
名称空间和作用域
名称空间(namespaces)
定义:存放名字(变量名、函数名等)的地方,是对栈区的划分
内存分为栈区和堆区,栈区存放的是函数的名称与数据地址的绑定关系,堆区存放的是数据本身(看可以简单的理解为栈区放的是变量名,堆区放的是变量值),其中栈区又被重新分区,分为内置名称空间、全局名称空间和局部名称空间,这样的管理即使出现相同名称的变量名,也能根据分区找到变量名(变量名查找的时候有一个优先级),不会产生冲突,其中全局名称空间和内置名称空间称为全局范围,局部名称空间称为局部范围,全局名称空间和内置名称空间都只有一个,局部名称空间可以有多个(先了解,后面会解释)
- 内置名称空间:存放的是解释器内置的名称(built-in function),在解释器开启的时候打开该空间,解释器关闭的时候关闭该空间。
- 全局名称空间:运行顶级代码产生的名字,或者说只要不是函数内定义的,或者是内置的名字,就都是全局名称空间的;在python程序的第三个阶段(运行文件代码)时产生,在文件运行结束之后关闭该空间。
- 局部名称空间:调用函数过程中,在函数代码运行的过程中产生的函数内的名字;该空间在函数调用时存活,在函数调用完毕时销毁(即使是一个函数调用了很多遍,也会产生多个局部名称空间)。
名称空间的加载顺序:先是加载内置名称空间,接着是全局名称空间,最后是局部名称空间
销毁顺序:先是局部的,然后是全局的,最后是内置的
名字的查找顺序:对于局部的名称,先在局部名称空间查找,接着是全局名称空间,最后是内置名称空间(可以理解为上下级的关系,后面会讲到函数的嵌套,本级的名称空间没有,调用的是上一级的名称空间),全局的名称直接在全局名称中查找,内置的名称直接在内置名称空间查找
函数的嵌套
def func1():
a='nihao'
def func2():
print(a)
# 这就是函数的嵌套,需要注意的是,局部名称空间也是一种嵌套的关系,当func2的局部空间中找不到a时,会到func1的空间中去找,使用func1的a,所以上面的结果应该是'nihao'
# 也可以使用函数功能直接进行嵌套
def max3(a,b,c):
res1=max(a,b)
res2=max(res1,c)
print(res2)
全局作用域和局部作用域
作用域就是作用范围
- 全局作用域:内置名称空间和全局名称空间对应的就是全局作用域
- 局部作用域:局部名称空间对应局部作用域
LEGB原则:l是local,指的是函数嵌套后的局部作用域的最内层,局部作用域除了最内层之外的都叫enclosing,全局作用域叫global,内置名称空间叫builtin
global和nonlocal
global可以在局部作用域修改全局变量的值(仅仅是对于局部作用域想要修改全局变量中不可变数据类型的值,对于可变的数据类型,局部作用域可以直接修改,底层逻辑允许)
nonlocal会将嵌套的函数内层的变量声明为外层的变量,使内层的局部作用域可以操作外层的变量
x=100
def func():
global x
x=200
print(x)
# 局部作用域只能引用全局变量,但不能改变全局变量(为了安全,修改了的话就是在局部名称空间中创建了一个同名的变量),global可以声明该变量变成了全局变量,从而进行修改
def func1():
x=100
def func2():
def func3():
nonlocal x=200
print(x)
# nonlocal是逐层向外找的(找不到就继续向外层找),global是直接在全局作用域找
globals()和locals()
- globals()返回一个字典,字典的内容是全局作用域中的所有对象
- locals()返回一个字典,显示的是当前作用域的所有对象
函数对象
就是可以将函数当成变量使用,函数名绑定的是内存地址
-
可以赋值给其他的变量
-
可以当做函数的参数传入
-
可以当做函数的返回值
-
可以当做容器类型的一个元素
# 函数对象的使用案例
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现')
def register():
print('注册')
func_dic = {
'0': ['退出', None],
'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('输入的指令不存在')
三元表达式
三元表达式是一种简化代码的方法
res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
def max2(x,y):
if x > y:
return x
else:
return y
# 下面进行简化:三元表达式
x=1
y=2
res = x if x > y else y # 只要是二选一的都可以使用

浙公网安备 33010602011771号