函数本质上就是一段可执行的代码,可以重复使用。
函数的创建
def 函数名(参数列表):
函数体
return (返回值)#可写可不写
- 函数名需要符合标识符的命名规则
- 参数可以为空
- 不带表达式的return相当于返回None。
参数传递
- 形参的位置在函数的定义处(a,b)
- 实参的位置是函数的调用处(2,3)
def f(x,y):
z=x+2*y
return z
#函数调用
result=f(2,3) #8
print(result)
res=f(y=2,x=3) #7 =左边的变量名称称为关键字参数
#res=f(x=3,2) 错误,缺少关键字y
print(res)
函数调用的参数传递
函数调用:
函数名(实际参数)
函数调用的2种方式:
- 位置传参
—根据形参对应的位置进行实参传递
如上面的例子就是位置实参,将2传给形参对应位置上的a,3传给b - 关键字传参
—根据形参名称进行实参传递(关键字不可缺少) - 序列传参(list,tuple,str类型)
—序列类型作为参数列表传递。要求序列元素个数=参数个数
def f(a,b,c):
print("a=",a,end=',')
print("b=", b,end=',')
print("b=", c)
lst=[6,7,8]#列表
f(*lst)#相当于f(lst[0],lst[1],lst[2])
t=(10,20,30)#元组
f(*t)
s='abc'#字符串
f(*s)
运行结果:
a= 6,b= 7,c= 8
a= 10,b= 20,c= 30
a= a,b= b,c= c
- 字典关键字传参
—实参和形参通过字典进行传递和匹配,字典的key为形参,value为实参(key的名字要和形参对应)
def f(a,b,c):
print("a=",a,end=',')
print("b=", b,end=',')
print("b=", c)
#d={'c':2,'a':1,'z':6} 字典关键字z在函数中没有叫z的形参
d={'c':2,'a':1,'b':6}
f(**d)
运行结果
a=1,b=6,c=2
在函数调用过程中,进行参数的传递。
若实参是不可变对象,在函数体的修改不影响实参的值(arg1的修改不影响n1的值)
若实参是可变对象,在函数体内的修改会影响实参的值(列表为可变对象,故arg2的修改会改变n2的值)
def fun(arg1,arg2):
print('arg1=',arg1)
print('arg2=',arg2)
arg1=100#将arg1重新赋值为100
arg2.append(10)#对arg2追加10
print('arg1=',arg1)
print('arg2=', arg2)
return
n1=11 #不可变对象
n2=[1,2,3] #可变对象
print('n1=',n1)
print('n2=',n2)
print('--------------------------')
fun(n1,n2)#n1,n2为形参arg1,arg2对应的实参,实参和形参名称可以不一致。位置传参
print('n1=',n1)
print('n2=',n2)
运行结果:
n1= 11
n2= [1, 2, 3]
--------------------------
arg1= 11
arg2= [1, 2, 3]
arg1= 100
arg2= [1, 2, 3, 10]
n1= 11
n2= [1, 2, 3, 10]
函数的返回值
- 若函数无返回值(函数执行完毕后,不需要给调用处提供数据,return可省略)
- 函数的返回值,如果是1个,直接返回原类型
- 函数的返回值,如果是多个,返回的类型为元组
函数的参数定义
默认值参数
函数定义式,对默认值参数(在定义形参时,给形参赋了值),则只有实参的值与默认值不符时,才需要传递(将实参的值赋给形参,形参的默认值被实参覆盖)
注意: 默认值参数需依次从右至左存在,即默认值参数的右侧应全为默认值参数
#def fun(a,b=10,c): 错误
def fun(a,b=10):#b有默认值10,称b为默认值参数
print(a,b)
#函数的调用
fun(100) #100,10
fun(100,30) #100,30 b的默认值被实参30覆盖了
不定长参数
- 个数可变的位置参数
- 定义函数时,可能无法事先确定传递的位置实参的个数,此时可使用可变的位置参数
- 用*定义个数可变的位置参数
- 返回结果为一个元组
- 个数可变的关键字参数
- 定义函数时,可能无法事先确定传递的关键字实参的个数,此时可使用可变的关键字参数
- 用**定义个数可变的关键字参数
- 返回结果为一个字典(键值对形式出现)
**注意:**函数定义时,个数可变的位置/关键字参数只能定义1个。函数定义时,既有个数可变的关键字形参又有个数可变的关键字形参,位置形参放在前面
def fun(*f):
print(f)
fun(1) #(1,)
fun(20,30,40) #(20, 30, 40)
fun([1,2,3]) #([1, 2, 3],)
def fun1(**f):
print(f)
fun1(a=1) #{'a': 1}
fun1(a=10,b=20,c=100) #{'a': 10, 'b': 20, 'c': 100}
def fun(*x,**y): #位置形参放在关键字形参之前
pass #没想好怎么写,先占位
!!!!:
将序列中的元素都转为位置实参:函数调用时使用*
将字典中的元素都转为关键字实参:函数调用时使用 **
def fun(a,b,c):#形参
print('a=',a)
print('b=',b)
print('c=',c)
fun(10,20,30)#位置传参 a= 10,b= 20,c= 30
lst=[11,12,13]
fun(*lst)#函数调用时,将列表中的每个元素转换为位置实参传入。 a= 11,b= 12,c= 13
fun(a=20,b=30,c=40)#关键字传参 a= 20,b= 30,c= 40
dic={'a':111,'b':122,'c':133}
fun(**dic)##函数调用时,将字典中的每个键值对转换为关键字实参传入。 a= 111,b= 122,c= 133
特定位置后的形参只能用关键字传参
'''需求:实参c,d只能采用关键字实参传递'''
def fun(a,b,*,c,d):#*号之后的形参只能采用关键字传参
print('a=',a)
print('b=',b)
print('c=',c)
print('d=',d)
fun(1,2,c=3,d=4)
函数定义时的顺序问题
几种参数定义方式混合使用时,遵循下面的顺序:
位置形参>*元组形参(个数可变的位置传参)>关键字传参>**字典形参(个数可变的关键字形参)>默认值参数
下面几种定义方式是合法的
def fun1(a,b,*,c,d,**args):
pass
def fun2(*args1,**args2):
pass
def fun3(a,b=10,*args):
pass
def fun4(a,*args,b,c):#b,c在*后面,所以调用时应该用关键字传参
pass
变量的作用域
变量的作用域指的是程序代码能访问该变量的区域。
分类
- 局部变量
- 在函数内定义并使用的变量
- 只在函数内部有效
- 若局部变量使用global声明,则变为全局变量。(global只能在函数内声明,否则无效)
- 全局变量
- 函数体外定义的变量
- 可作用于函数内外
def fun1(a,b):
c=a+b#c为局部变量,因为c是在函数体内定义的变量。a,b为形参,作用范围也为函数内部,相当于局部变量
print(c)
#print(c) 报错,因为超出了起作用的范围。
name='anna'#name为全局变量
print(name)
def fun2():
print(name)
fun2()
def fun3():
global age #age为函数内部定义的变量,为局部变量。用global声明,故变为全局变量
age=10
print('age=',age)
# 调用函数
fun3()#age=10
print('age=',age)#age=10
def fun3(a,b):
global c
c=a+b
print('c=',c)
# 调用函数
fun3(1,2)#c=3
print('c=',c)#c=3
递归函数
定义
在函数的函数体内部调用了函数本身
递归的组成部分
- 递归调用条件
- 递归终止条件
调用过程
- 每调用一次函数,都在栈内分配一个栈帧
- 每执行完一次调用后都释放相应空间
优缺点
- 占内存,效率低
- 思路/代码简单
例:用代码计算6!=720
def fac(n):
if n!=1:
res=n*fac(n-1)
return(res)
else:
return 1
print(fac(6))