一、函数的使用原则
函数的使用应该分为两个明确的阶段
1. 定义阶段:只检测语法,不执行函数体代码
def func():
print('from func')
2. 调用阶段:会触发函数体代码的执行
func()
函数使用的原则: 先定义后调用
示范一
def foo():
print('from foo')
bar() bar函数在这里不能直接调用,违背了函数使用的原则,调用阶段程序会进行报错
foo()
# 示范二:
def bar():
print('from bar')
def foo():
print('from foo')
bar() 在这里程序会正常运行,bar函数在被foo调用之前已经被定义过了
foo()
# 示范三:
def foo(): 示范三里面虽然bar函数定义在后面,但是函数定义阶段是只检测语法不执行代码的,在语法上并没有错误,所 以在调用foo函数的时候,bar函数已经定义好了,在foo函数内再调用也不会出现错误
print('from foo')
bar()
def bar():
print('from bar')
foo()
示范四:
def foo():
print('from foo')
bar()
foo() 示范四就会进行报错了,在调用foo的时候,bar函数还没有定义完成
def bar():
print('from bar')
二、函数的定义三种形式
有参函数
def func(x):
pass
func(1)
无参函数
def bar():
pass
bar()
空函数
def func():
pass
def auth():
pass
三、函数调用的三种形式
register() # 语句形式
def max2(x,y):
if x > y:
return x
else:
return y
res=max2(10,20)*12 # 表达式形式
res=max2(max2(10,20),30) # 将函数的调用当作参数传给另外一个函数
print(res)
四、函数的返回值(return)的使用
return 值:
注意点:
1. 函数的返回值没有类型限制
2. 函数的返回值没有个数限制
2.1 返回多个值: 多个返回值用逗号分隔开,返回的是元组形式
def func():
print('from func')
return 1,1.1,'hello',[1,2,3]
res=func()
print(res,type(res)) 输出为:(1, 1.1, 'hello', [1, 2, 3]) <class 'tuple'>
2.2 返回1个值: 返回的就是该值本身
def func():
return 123
res=func()
print(res,type(res)) 输出:123 <class 'int'>
2.3 返回0个值或者干脆没有return: 返回None
def func():
return
pass
res=func()
print(res) 输出:None
return除了有返回值的功能,还有结束函数执行的的功能
函数内可以有多个return,但只要执行一次,整个函数就立即结束,并且将return后的值返回
def func():
print(1)
return 在调用函数的时候,在运行到return时真个函数就会立即结束,并将return的值返回
print(2)
return
print(3)
func()
五、函数参数的使用
一: 函数的参数分为两大类:
形式参数(形参): 在定义函数阶段,括号内定义的参数/变量名称为形参
实际参数(实参): 在调用函数阶段,括号内传入的值/变量值称为实参
ps: 在调用函数阶段会将实参(值)的值绑定给形参(变量名),这种绑定关系只在调用函数时生效,在函数执行完毕后就会解除绑定
def func(x,y): #x=1 y=2
# x=1 (定义参数时,相当于在这里加上了两行变量的定义)
# y=2
print(x,y)
func(1,2)
print(x)
print(y)
二: 细分:
1. 位置参数:(位置形参和位置实参)
1.1 位置形参: 在定义阶段,按照从左到右的顺序依次定义的形参称之为位置形参
特点: 但凡是按照位置定义的形参,必须被传值,多一个不行少一个也不行
def func(x,y,z):
print(x,y,z)
func(1,2) 调用时程序会报错
func(1,2,3)
func(1,2,3,4) 调用时程序会报错
1.2 位置实参: 在调用阶段,按照从左到右的顺序依次传入的值称之为位置实参
特点:
1. 与形参一一对应
def func(x,y,z):
print(x,y,z)
func(2,1,3)2. 关键字实参: 在调用阶段,按照key=value的形式定义的实参称之为关键字实参
特点: 可以完全打乱顺序,但仍然能为指定的形参传值(总结:指名道姓地为指定的形参传值)
def func(x,y,z):
print(x,y,z)
func(x=1,y=2,z=3)
func(1,2,3)
func(z=3,y=2,x=1)
实参的形式可以是位置实参与关键字实参混合使用,但是必须遵循原则
1.位置实参必须放在关键字实参的前面
2.不能对同一个形参重复传值
func(1,z=3,y=2)
func(z=3,1,y=2) #错误
func(1,z=3,x=2,y=3) #错误
3. 默认形参:在定义阶段,就已经为形参赋值,该形参称之为默认形参
特点
1. 定义阶段就已经有值, 意味着调用阶段可以不用传值
def register(name,age,sex='male'):
print(name,age,sex)
register('egon',18,) 在这里前四个都使用默认的sex值就可以,调用函数的时候,不需要再给
sex赋值,减少调用时的麻烦
register('lxx',38,)
register('cw',48,)
register('yyh',58,)
register('alex',73,'female') 在需要改变sex的值时,重新给一个值就可以了
def func(x,y,z=100):
print(x,y,z)
func(10,20)
func(10,20,200)
2. 位置形参必须放到默认形参的前面
def func(x,z=100,y): 这样的位置程序会进行报错,将默认形参放到位置形参后边即可解决
print(x,y,z)
3. 默认形参的值在函数定义阶段就已经固定死了,定义阶段之后的改动不会影响该值
m=10
def func(x,y,z=m):
#z=10
print(x,y,z)
m=100 在这里再改变m的值不会对函数中m的使用产生影响
func(1,2)
4. 默认形参的值通常应该是不可变类型
def add_hobby(name,x,hobbies=[]):
hobbies.append(x)
print('%s 的爱好有 %s' %(name,hobbies))
add_hobby('egon','read',) 输出结果:egon 的爱好有 ['read']
add_hobby('wxx','eat',) wxx 的爱好有 ['read', 'eat']
add_hobby('alex','piao') alex 的爱好有 ['read', 'eat', 'piao'] 有这样的结果是因为,在初始时,hobbies里边是一个空列表,写入第一个egon的爱好进去之后,就变成了['read'],再调用函数时,hobies就变成了默认形参,hobbies=['read'],这时再使用.append将'eat'加入进去时,就变成了['read','eat']
def add_hobby(name,x,hobbies=None): 解决方案
if hobbies is None:
hobbies=[]
hobbies.append(x)
prinvt('%s 的爱好有 %s' %(name,hobbies))
4. 可变长参数:
可变长实参:指的是在调用阶段,实参值个数是不固定的,
实参无非两种形式(位置,关键字实参),对应着形参也必须有两种解决方案来分别接收溢出位置实参或者关键字实参
*--->溢出的位置实参
**--->溢出的关键字实参
def sum2(*x): #x=(1,2,3,4,5)
res=0
for i in x:
res+=i
return res
print(sum2(1,2,3,4,5))
4.1 *的用法
在形参前加*:*会将溢出的位置实参存成元组的形式,然后赋值给*后的形参名
def func(x,y,*z): #z=(3,4,5)
print(x,y,z)
func(1,2,3,4,5)
在实参前加*:但凡碰到实参中带*的,先将实参打散成位置实参再与形参做对应
def func(x,y,z):
print(x,y,z)
func(1,2,[3,4,5])
func(*[1,2,3,4,5]) #func(1,2,3,4,5) 在这里执行'打散'操作
func(*[1,2,3]) #func(1,2,3)
def func(x,y,*z):
print(x,y,z)
func(1111,2222,*[1,2,3,4,5]) #func(1111,2222,1,2,3,4,5)
4.2 **的用法
在形参前加**:**会将溢出的关键字实参存成字典的形式,然后赋值给**后的形参名
def func(x,y,**z): #z={'c':3,'b':2,'a':1}
print(x,y,z)
func(1,y=2,a=1,b=2,c=3)
在实参前加**:但凡碰到实参中带**的,先将实参打散成关键字实参再与形参做对应
def func(x,y,z):
print(x,y,z)
func(1,**{'y':2,'z':3}) #func(1,z=3,y=2)
func(1,**{'a':2,'y':333,'z':3}) #func(1,a=2,y=333,z=3) # 错误
def func(x,y,**z):
print(x,y,z)
func(**{'y':1,'x':2,'a':1111,'b':2222}) #func(y=1,x=2,a=1111,b=2222)
形参中:*args,**kwargs
def func(x,*args):
print(x)
print(args)
def func(x,**kwargs):
print(x)
print(kwargs)
*与**的应用:
def index(name,age,sex):
print('index=====>',name,age,sex)
会将wrapper函数接收的参数格式原封不动地转嫁给其内部的index函数,必须要遵循的是index的参数规则
这时*args和**kwargs可以接受任意类型的值
def wrapper(*args,**kwargs): #args=('egon',) kwargs={'sex':'male','age':18}
print(args)
print(kwargs)
index(*args,**kwargs) #index(*('egon',),**{'sex':'male','age':18}) #index('egon',sex='male',age=18)
wrapper(1,2,3,4,5,a=1,b=2,c=3)
wrapper('egon',sex='male',age=18)