一、函数
一、什么是函数
函数就是具有某个具体功能的工具
二、为什么要用函数
减少代码冗余
提供开发效率
提高程序的扩展性
三、定义一个函数
def是定义函数的关键字
函数名:函数名的命名规则与变量名的命名规则一样
1.不能以关键字命名
2.函数名也要做到见名之意
函数在定义的时候只检测语法,不会执行代码
调用函数的固定格式 :
函数名+括号
函数名只要遇到括号会立即执行函数代码
代码中遇到函数名加括号 优先级最高
先去执行函数 再去看下面的代码
二、函数的返回值 return
函数内要想返回给调用者,必须用return
有五种情况:
1.不写return :函数默认返回None
def func(): print(1+2) #3 res1 = func() print(res1) #None
2.只写return:也是返回None,除了有返回值,还可以直接结束整个函数运行,不管嵌套了几层循环都会被结束。
print(1+2)不会执行,因为在它前面有个return,之后的所有代码都不会执行,最终结果只返回None
def func(): return print(1+2) res1 = func() print(res1) #None
3.写return None:也是返回None,和只写return一样也会结束函数运行
def func(): return None print(1+2) res1 = func() print(res1) #None
4.写return 返回一个值:这个值可以是任何数据类型
def func(): return '1234' def func1(): return [1,2,3] def func2(): return (1,2,3,4) def func3(): return {'name':'aaa'} print(func(),func1(),func2(),func3()) #1234 [1, 2, 3] (1, 2, 3, 4) {'name': 'aaa'}
5.写return 返回多个值:return 会自动将多个值以元组的形式返回给调用者
def func(): return '1','2','3' def func1(): return [1,2,3],[4,5,6],[7,8,9] def func2(): return (1,2,3,4),(1,2,3) def func3(): return {'name':'aaa'},{'age':18} print(func(),func1(),func2(),func3()) #('1', '2', '3') ([1, 2, 3], [4, 5, 6], [7, 8, 9]) ((1, 2, 3, 4), (1, 2, 3)) ({'name': 'aaa'}, {'age': 18})
如果不想以元组的方式返回,那就手动加上你想返回的数据类型的符号
def func4(): return [(1,2,3,4),(1,2,3)] print(func4()) #[(1, 2, 3, 4), (1, 2, 3)]
总结:1.所有函数都有返回值,不写return返回值是None
2.光写return 或者return None不是考虑函数返回值,而是函数结束运行
三、函数参数 四种:位置参数、关键字参数、默认值参数、可变长参数(不定长参数)
函数参数的两大类型:
形参:在函数定义阶段 括号内写的变量名,叫做该函数的形式参数
实参:在函数调用阶段 括号内传入的实际值,叫做实际参数
形参与实参的关系:
形参就相当于变量名,实参就相当于变量名的值
函数调用传参的过程,就是给形参赋值的过程
注意:形参和实参的绑定关系只在函数的调用阶段有效,函数运行结束关系自动解除,只有在函数内部有效,函数外部无任何影响
现在我们可以得出函数的简易结构:
def 函数名(形参1,形参2。。。):
'''函数注释'''
函数代码体1
函数代码体2
。。。
return 函数的返回值
1.位置参数:在函数定义阶段按照位置从左往右依次书写的变量名,叫做位置形参
位置形参:在调用的时候 必须为其传值
def func(x,y): return x+y res = func(1,2) print(res) #3
位置实参:在调用函数的时候,多一个参数,少一个参数都不行,传入的参数要按照位置一一对应传给形参
2.关键字参数:在函数调用阶段,给变量名赋值。
def func(x,y): return x+y res = func(3,y=2) print(res) #5
总结:
注意:在函数调用阶段,位置参数和关键字参数可以混合使用
但必须保证:
1.位置参数必须在关键字参数前面(越短的越靠前,越长的越复杂的越靠后),在函数定义阶段这个也适用
2.同一个参数不能被多次赋值
3.默认值参数:在函数定义阶段,给变量名赋值
在调用函数阶段,如果不给该参数(变量名)传值,就默认使用在定义阶段赋值的值。
在调用函数阶段,如果给该参数(变量名)传值,就使用你传的值。
在定义阶段也是位置参数放在前面,默认值参数放在后面(越短的越在前面,越长的越在后面)
#在调用的时候,不给y传值 def func(x,y=3): return x+y res = func(3) print(res) #6 #在调用的时候,给y传值 def func(x,y=3): return x+y res = func(3,y=6) print(res) #9
易错题分析(这里一直是在使用唯一一个列表,所以会一直往一个列表里面添加)
def info(username,hobby,l=[]): l.append(hobby) print('%s 的爱好是 %s'%(username,l)) info('jason','study') info('tank','生蚝') info('kevin','喝腰子汤') info('egon','女教练') #结果 #jason 的爱好是 ['study'] #tank 的爱好是 ['study', '生蚝'] #kevin 的爱好是 ['study', '生蚝', '喝腰子汤'] #egon 的爱好是 ['study', '生蚝', '喝腰子汤', '女教练']
纠正(在调用info的时候,默认传值l = None,函数体里面if判断如果l == None,就会变成l = [],每一次调用都会生成一个)
def info(username,hobby,l=None): if l == None: l = [] l.append(hobby) print('%s 的爱好是 %s'%(username,l)) info('jason','study') info('tank','生蚝') info('kevin','喝腰子汤') info('egon','女教练') #结果 #jason 的爱好是 ['study'] #tank 的爱好是 ['生蚝'] #kevin 的爱好是 ['喝腰子汤'] #egon 的爱好是 ['女教练']
总结:函数无论在什么地方被调用,都会到函数定义阶段去执行代码,所需要值都是往函数定义阶段上面找。
函数在定义阶段,内部所使用的的变量都已经全部初始化完毕了,不会因为后面调用位置的改变而影响到内部的值
4.可变长参数:在调用的时候,实参传递的个数不固定,可能会传递多了,也可能会传递少了,在定义的时候,形参也可能会这样,我们用*和**来接收多余的参数
1.站在形参的角度看 *
形参中的*会将实参中的多余位置参数统一放在元组中,然后传递给后面的形参名
*在形参中只能接受多余的位置参数,其他的类型参数不行
def func(x,y,*z): print(x,y,z) func(1,2,3,4,5,6) #1 2 (3, 4, 5, 6)
2.站在实参的角度看 *
实参中的*会将元组、列表、集合、字符串打散,然后一一传入到形参中,打散之后的实参个数要和形参个数一致,不然就会报错
func(*(1,2,3)) 等价于 func(1,2,3)
def func(x,y,z): print(x,y,z) func(*(1,2,3)) #1 2 3
3.站在形参角度看 **
形参中的 **会将多余的关键字参数(实参)统一放在一个字典中,然后传递给后面的形参名
**在形参中只接受多余的关键字参数,其余类型参数不行
def func(x,y,**z): print(x,y,z) func(1,2,a=3,b=4) #1 2 {'a': 3, 'b': 4}
4.站在实参的角度看 ** 将实参中传入的字典转换成 key = value
func(**{'x':1,'y':2,'z':3}) 等价于func(x=1,y=2,z=3)
特别重要的一点:在实参中传参的时候这个字典的key要和形参中的变量名取名一致
def func(x,y,z): print(x,y,z) func(**{'x':1,'y':2,'z':333}) #1 2 333
总结 *和**
*在形参中能够接受实参中多余的位置参数,并且把他们统一放在一个元组中并赋值给*后面的变量名
**在形参中能够接受实参中多余的关键字参数,并且把他们统一成一个字典并赋值给**后面的变量名
*在实参中能够将列表,元组,字符串,集合打散成位置参数形式传递给形参
**在实参中能够将字典打散成key = value 的形式 ,以关键字参数传递给形参
本节总结:
1.关键字参数是在实参中设定
2.默认参数是在形参中设定