函数的参数
形参和实参:
形参是在定义函数时,括号内声明的参数,形参本质就是一个变量名,用来接收外部传来的值
实参是在调用函数时,括号内传入的值,实参就相当于变量的值
形参与实参的关系:
在调用阶段,实参(变量值)会绑定给形参(变量名)
实参和形参的绑定关系只能在函数体内使用
这种绑定关系在调用时生效,调用用结束后解除
形参和实参的使用:
位置参数:
位置形参:
在函数定义阶段。按照从左到右的顺序依次定义参数,称之为位置形参,必须被传值,多一个不行少一个也不行
def func(x,y):
print(x,y)
位置实参:
在函数调用阶段,按照从左到右的顺序以此定义的实参,称之为实参,按照顺序与形参一一对应
func(1,2)
关键字参数:
在调用函数时,按照key=value的形式为指定的参数传值,可以完全不按照从左到右的顺序定义,关键字参数必须在位置参数后面,且不可以对一个形参重复赋值
def func(x,y):
print(x,y)
func(y = 1,x = 2)
默认参数:
在定义函数时,就已经为形参赋值,这类形参称之为默认参数,默认参数必须在位置参数之后,默认参数的值仅在函数定义阶段被赋值一次,默认参数的值通常应设为不可变类型因为如果发生改变就失去了默认参数的意义。
x=1
def foo(arg=x):
print(arg)
x = 5
print(foo) #结果仍然为1
可变长度的参数:
参数的长度可变指的是在调用函数时,实参的个数可以不固定
可变长度的位置参数:
如果在最后一个形参名前加*号,那么在调用函数时,溢出的位置实参,都会被接收,以元组的形式保存下来赋值给该形参,*后跟的可以是任意名字,但是约定俗成应该是args
def foo(x,y,*args):
print(x)
print(y)
print(args)
foo(1,2,3,4,5,6,7)
多值求和案例:
def add(*args):
res=0
for i in args:
res+=i
return res
add(1,2,3,4,5)
可以用在实参中,实参中带*,先把*后的值打散成位置实参
def func(x,y,z):
print(x,y,z)
func(*[11,22,33]) #其结果为11,22,33
可变长度的关键字参数:
如果在最后一个形参名前加**号,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参
def foo(x,**kwargs): #在最后一个参数kwargs前加**
print(x)
print(kwargs)
foo(y=2,x=1,z=3) #溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
1
{'z': 3, 'y': 2}
实参中的**,会将**后参数的值循环取出,打散成关键字实参。以后但凡碰到实参中带**的,他就是关键字实参,应该马上打散成关键字实参去看
def func(x,y,z,):
print(x,y,z)
func(**{'x':1,'y':2,'z':3})
#1 2 3
def func(x,y,z,**kwargs):
print(x,y,z,kwargs)
func(1,**{'y':1,'z':2,'a':3,'b':4})
# 1 1 2 {'a': 3, 'b': 4}
强调:混用*与**
在该函数内部还可以把接收到的参数传给另外一个函数
def func(x,y,z):
print(x,y,z)
def wrapper(*args,**kwargs):
func(*args,**kwargs)
wrapper(1,z=3,y=2)
1 2 3
按照上述写法,在为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:
位置实参1被接收,以元组的形式保存下来,赋值给args,即args=(1,),关键字实参z=3,y=2被*接收,以字典的形式保存下来,赋值给kwargs,即kwargs={'y': 2, 'z': 3}
执行func(args,kwargs),即func((1,),* {'y': 2, 'z': 3}),等同于func(1,z=3,y=2)
名称空间与作用域
名称空间:
名称空间即存放名字与对象映射/绑定关系的地方,在程序执行期间最多会存在三种名称空间。
内置名称空间:
伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名
>>> input
<built-in function input>
全局名称空间:
伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中,不是函数内定义也不是python内置的,就是全局名称空间。
import sys #模块名sys
x=1 #变量名x
if x == 1:
y=2 #变量名y
def foo(x): #函数名foo
y=1
def bar():
pass
Class Bar: #类名Bar
pass
局部名称空间:
伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中,调用函数运行函数体代码时产生的函数内的名字。
def foo(x):
y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
关于名称空间的顺序:
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间
名称空间的查找(销毁)顺序为:局部名称空间->全局名称空间->内置名称空间
作用域
全局作用域与局部作用域:
全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用);
# x=111
#
# def foo():
# print(x,id(x))
#
# def bar():
# print(x,id(x))
#
# foo()
# bar()
# print(x,id(x))
局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)。
# def foo(x):
# def f1():
# def f2():
# print(x)
# LEGB
# # builtin
# # global
# def f1():
# # enclosing
# def f2():
# # enclosing
# def f3():
# # local
# pass
local和global:
#如果再局部想要修改全局的名字对应的值(不可变类型),需要用global
# x=111
#
# def func():
# global x # 声明x这个名字是全局的名字,不要再造新的名字了
# x=222
#
# func()
# print(x)
# 示范3:
# l=[111,222]
# def func():
# l.append(333)
#
# func()
# print(l)
# nonlocal(了解): 修改函数外层函数包含的名字对应的值(不可变类型)
# x=0
# def f1():
# x=11
# def f2():
# nonlocal x
# x=22
# f2()
# print('f1内的x:',x)
#
# f1()
#def f1():
# x=[]
# def f2():
# x.append(1111)
# f2()
# print('f1内的x:',x)
#
#f1()