前言
上篇文章 python学习——【第七弹】学习了python中的可变序列集合,自此python中的序列的学习就完成啦,这篇文章开始学习python中的函数
。
函数
在学习其他编程语言的时候我们就了解过函数:函数就是执行特定任何以完成特定功能的一段代码
那么我们为什么要使用函数呢?
复用代码
隐藏实现细节
提高可维护性
提高可读性便于调试
函数的创建
python中函数的创建方式:
def 函数名 ([输入参数]):
函数体
[return xxx]
形参:形参出现在函数的定义处
实参:实际参数的值,位置出现在函数的调用处
函数的调用
在学习其他的语言时我们知道,函数只有在调用的时候才能执行,python中的函数也是一样的。调用函数的方式也很简单:
[返回值] = 函数名([参数])如果函数没有参数,则括号中可以留空
def pri(): print('helloworld')pri()#调用函数
那么在调用函数时,对调用的参数有什么要求呢?
函数调用的参数传递
位置实参:根据形参对应的位置进行实参传递
def add(a,b): num=a+b return numresult=add(10,20)print(result) #30
关键字实参:根据形参名称进行实参传递
def add(a,b): num=a+b return numresult=add(b=10,a=20)print(result) #30
需要注意的是,创建函数有多少个形参,那么调用时就需要传入多少个值,且顺序必须和创建函数时一致。即便该函数没有参数,函数名后的小括号也不能省略。
函数参数的定义
函数定义时,给形参设置默认值,只有与默认值不符的时候才需要传递实参
def fun1(a,b=10): print(a,b)# 函数调用fun1(100) #100 10fun1(20,30) #20 30 与默认形参值不相符,传递实参值
个数可变的位置形参
1:定义函数时,可能无法事先确定传递的位置的实参的个数,可以使用可变的位置参数
2:使用 * 定义个数可变的位置形参
3:结果为元组类型
4:如果没有需传递的位置实参,但是使用了可变的位置参数,这时再调用该函数,会返回一个空元组 即args=()
def fun1(*args): print(args,type(args))fun1(10)fun1(10,20)fun1(10,20,30)'''(10,) <class 'tuple'>(10, 20) <class 'tuple'>(10, 20, 30) <class 'tuple'>'''def fun11(x,y,*args): #已知形参(x,y)要放在位置形参之前;否则会报 SyntaxError: invalid syntax 无效语法错误 print('x=',x) print('y=',y) print('args=',args)fun11(11,22,33,44,55)'''x= 11y= 22args= (33, 44, 55)'''
注意:使用个数可变的位置参数时,可变的位置参数只能是1个
个数可变的关键字形参
1:定义函数时,无法事先确定确定传递的关键字实参的个数时,使用可变的关键字形参
2:使用 ** 定义个数可变的关键字形参
3:结果是一个字典
4:如果没有需要传递的关键字实参,但是使用了可变的关键字参数,这时再调用该函数,会返回一个空字典 即args={}
def fun2(**args): print(args,type(args))fun2(a=10)fun2(a=10,b=20,c=30)'''{'a': 10} <class 'dict'>{'a': 10, 'b': 20, 'c': 30} <class 'dict'>'''def fun22(x,y,**args): #已知形参(x,y)要放在关键字形参之前;并且位置实参的个数要和位置形参的个数保持一致,否则会报:TypeError: fun22() takes ... positional arguments but ... were given 实参数与形参数不符 print('x=',x) print('y=',y) print('args=',args)fun22(11,22,c=10,d=0)'''{'a': 10}{'a': 10, 'b': 20, 'c': 30}'''
注意:使用个数可变的关键字形参时,可变的关键字形参只能是1个
def fun6(**args,**args): pass# 程序报错 SyntaxError: invalid syntax 无效语法
定义函数的过程中,既有个数可变的关键字形参,也有个数可变的位置形参,要求个数可变的位置形参放在个数可变的关键字形参之前。
def fun(x, y, *args, **argss): print("x=", x) print("y=", y) print("args=", args) print("argss=", argss)fun(1, 2, 3, 4, e=5, f=6, g=7) '''x= 1y= 2args= (3, 4)argss= {'e': 5, 'f': 6, 'g': 7}'''# 可变位置参数和可变关键字参数需要传递的实参数不相匹配时,如果可变位置参数或可变关键字参数没有收到需要传递的实参的值的话,调用函数时分别返回空元组和空字典。def fun(x, y, *args, **argss): print("x=", x) print("y=", y) print("args=", args) print("argss=", argss)fun(3, 4) '''x= 3y= 4args= ()argss= {}'''
函数参数的传递
位置参数的传递
def funs(a,b,c): print('a=',a) print('b=',b) print('c=',c)funs(10,20,30)lst=[11,22,33]funs(*lst)# 使用位置参数传递,在函数调用时,将列表中的每个元素都转换为位置参数传入'''a= 10b= 20c= 30a= 11b= 22c= 33'''
关键字参数的传递
# 关键字参数传递funs(a=111,b=222,c=333)dic={'a':444,'b':555,'c':666}funs(**dic)# 使用关键字参数传递,在函数调用时,将字典中的每个元素的键值对都转换为关键字参数传入'''a= 111b= 222c= 333a= 444b= 555c= 666'''
在 * 之后的参数,在函数调用时,只能采用关键字参数进行传递
def funcs(a,b,*,c,d): print('a=',a) print('b=',b) print('c=',c) print('d=',d)funcs(10,20,c=30,d=40)'''a= 10b= 20c= 30d= 40'''
函数传递时形参的位置问题
函数的参数总结
1 :将序列中的每个元素都转换为位置实参
使用*
2 :将字典中的每个键值对都转换为关键字实参
使用 **
3 : 关键字形参 使用*
4 :个数可变的位置形参 使用*
5 :个数可变的关键字形参 使用**
函数调用参数传递内存分析
def fun(num1,num2): print('num1=',num1,id(num1)) #num1= 90 140471326216032 print('num2=',num2,id(num2)) #num2= [11, 22, 33, 44] 140471317443120 num1=111 num2.append(999) print('num1=',num1,id(num1)) #num1= 111 140471326216704 print('num2=',num2,id(num2)) #num2= [11, 22, 33, 44, 999] 140471317443120print('---------调用函数之前---------')add1=90add2=[11,22,33,44]print(add1,id(add1)) #90 140471326216032print(add2,id(add2)) #[11, 22, 33, 44] 140471317443120print('-------------调用函数----------')fun(add1,add2)'''num1= 90 140471326216032num2= [11, 22, 33, 44] 140471317443120num1= 111 140471326216704num2= [11, 22, 33, 44, 999] 140471317443120'''print('-----------调用函数后----------')print(add1,id(add1)) #90 140471326216032print(add2,id(add2)) #[11, 22, 33, 44, 999] 140471317443120
可以发现:在函数调用过程中,若要进行参数传递:
如果是可变对象(如列表、集合、字典)
,在函数体内对其进行修改会影响
到函数体外的这个可变对象的值(因为可变对象在内存中是可变的,可以被修改),其id地址不会发生改变
;
如果是不可变对象(如数值、字符串、元组)
,在函数体内对其进行修改不会影响
到函数体外这个不可变对象的值(因为不可变对象在内存中是固定的,无法被修改),其id地址不会发生改变
。
函数返回值
1:当函数不需要返回值时,函数体内可以省略return;并且函数执行完毕后,不需要给调用处提供数据
def pri(): print('helloworld')pri()
2:当函数只有一个返回值时,返回值类型就是原类型
def fun2(): return 'hello'res=fun2()print(res,type(res)) #hello <class 'str'>
3:当函数有多个返回值时,返回值类型是一个元组
def fun(num): odd=[] even=[] for i in num: if i%2==0: odd.append(i) else: even.append(i) return odd,evenlist1=[11,22,32,23,45,6,0]print(fun(list1),type(fun(list1)))# ([22, 32, 6, 0], [11, 23, 45]) <class 'tuple'>
变量的作用域
程序代码能访问该变量的区域 称作变量的作用域
根据变量的有效范围可分为:
局部变量
在函数体内定义并使用的变量,只在函数内部有效,局部变量使用global
声明 , 这个变量就会成为全局变量
def func1(a,b): c=a+b #c 就成为局部变量 因为c是在函数体内进行定义的变量 a,b是函数的形参 作用范围也是函数内部 相当于局部变量 return cprint(func1(1,2))#3
# 使用global 声明局部变量,a b为全局变量,在函数体内可以直接被调用;c在函数体内定义,属于局部变量,因此需要用global声明才能在函数体外被调用a=1b=2def func3(): global c c=a-b return cprint(func3()) #-1
全局变量
函数体外定义的变量 ,可作用于函数体内
# 全局变量 a=1,b=2为定义在函数体外的变量,属于全局变量,在函数体内与函数体外均可调用a=1b=2def func2(): c=a+b return cprint(func2()) #3
递归函数
什么是递归函数
: 如果在一个函数的函数体内调用了该函数本身, 这个函数就称作递归函数
递归的组成部分
: 递归调用与递归终止条件
递归的调用过程
: 每递归调用一次函数 ,都会在栈内存分配一个栈帧(每次调用函数时,在内存中都会单独开辟一个空间,配合函数运行,这个空间叫做栈帧空间。)每执行完一次函数, 都会释放相应的空间
递归的思想
:
递归是一去一回的过程,调用函数时,会开辟栈帧空间,函数执行结束之后,会释放栈帧空间,递归实际上就是不停地开辟和释放栈帧空间的过程,每次开辟栈帧空间,都是独立的一份,其中的资源不共享。
递归其实利用的时是压栈的思想,我们看一下下面这个例子:
压栈思想:
递归的优缺点
优点:
递归函数使代码看起来干净整洁。使用递归可以将复杂的任务分解为更简单的子问题。与使用嵌套嵌套相比,使用递归更容易生成序列。
缺点:
有些情况,递归背后的逻辑很难遵循。递归调用很昂贵(效率低),因为它们占用大量内存和时间。递归函数调试较为复杂。
递归函数的应用
利用递归函数计算阶乘
def func1(n): if n == 1: return 1 else: return n*func1(n-1)print(func1(6)) #720
利用递归函数计算 斐波那契数列
斐波那契数列:1、1、2、3、5、8、13、21、34。。。
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
计算第n位的斐波那契数是多少
def func2(n): if n == 1: return 1 elif n == 2: return 1 else: res=func2(n-1)+func2(n-2) return resprint(func2(6)) #8
打印出整个斐波那契数列
for i in range(1,7): print(func2(i),end=' ')print('\n')#1 1 2 3 5 8
键盘录入一个整数,然后根据这个正整数打印出斐波那契数列以及其数列和
num=int(input('请输入一个正整数:'))def func3(num): if num==1: return 1 elif num==2: return 1 else: result=func3(num-1)+func3(num-2) return resultnums=[]sums=0for n in range(1,num+1): # print(func3(n),end=' ') nums.append(func3(n)) sums+=func3(n)print('产生的斐波那契数列是:',nums)print('\n第',num,'位的斐波那契数为:',func3(num))print('\n斐波那契数列之和为:',sums)
每篇一语
努力的人运气都不会太差!
如有不足,感谢指正!