Python基础(函数)
一、函数的创建和调用
什么是函数
- 函数就是执行特定任务和以完成特定功能的一段代码
为什么需要函数
- 复用代码
- 隐藏实现细节
- 提高可维护性
- 提高可读性便于调试
函数的创建
def 函数名([输入参数]):
函数体
[return xxx]
def calc(a,b):
c=a+b
return c
函数的调用
def calc(a,b):
c=a+b
return c
result=calc(10,20)
print(result)
输出:30
执行步骤:
- 跳到定义函数的函数体内
- 执行函数体
- 跳到函数的调用处
- 继续执行下一条语句
二、函数的参数传递
位置实参
- 根据形参对应的位置进行实参传递
print('-----函数的创建-----')
def calc(a,b): #a,b称为形式参数,形参的位置在函数的定义处
c=a+b
return c
result=calc(10,20) #10,20是实际参数的值,称为实参,实参的位置在函数的调用处
print(result)
a占第一个位置,b占第二个位置
则调用处第一个位置的10 传递给a,调用处第二个位置的传递给
关键字实参
- 根据形参名称进行实参传递
re=calc(b=21,a=11)
print(re)
输出:32
函数参数传递的内存分析
def fun(arg1,arg2):
print('arg1',arg1)
print('arg2',arg2)
arg1=100
arg2.append(10)
print('arg1',arg1)
print('arg2',arg2)
n1 = 11
n2 = [22,33,32]
print('n1',n1)
print('n2',n2)
fun(n1,n2)
print('n1',n1)
print('n2',n2)
输出:
n1 11
n2 [22, 33, 32]
arg1 11
arg2 [22, 33, 32]
arg1 100
arg2 [22, 33, 32, 10]
n1 11
n2 [22, 33, 32, 10]
如果是不可变对象,在函数体的修改不会影响实参的值,arg1的修改为100,不会影响n1的值
如果是可变对象,在函数体的修改会影响到实参的值,arg1的修改,append(10),会影响到n2的值
三、函数的返回值
函数返回多个值时,结果为元祖
def func(num):
odd = [] #存奇数
even = [] #存偶数
for i in num:
if i%2:
odd.append(i)
else:
even.append(i)
return odd,even
print(func([10,29,34,23,44,53,55]))
输出:
([29, 23, 53, 55], [10, 34, 44])
将两个列表当元素组成了元祖
函数返回值
-
如果函数没有返回值(函数执行完毕之后,不需要给调用处提供数据),return可以省略不写
-
函数的返回值,如果是1个,直接返回类型
-
函数的返回值,如果是多个,返回的结果为元祖
def func1(): print('hello') res = func1() print(res)
输出:
hello
None
因为func1 没有返回值,所以为None
def func2():
return 22
res2=func2()
print(res2)
输出:
22
有一个返回值
四、函数的参数定义
函数定义默认值参数
def func3(a,b=10): #b称为默认值参数
print(a,b)
#函数的调用
func3(100) #只传一个参数,b的值为默认值
func3(20,30) #传了两个参数,b的默认值被覆盖
这里给参数b设置了个默认值
输出:
100 10
20 30
其实参数里面有个隐形默认值
print('hello')
print('kitty')
print('hello',end='\t')
print('world')
输出:
hello
kitty
hello world
其实是有个隐形参数 end= ,默认是换行,但是可以换成 ‘\t’ 表示tab
个数可变的位置参数
-
定义函数时,可能无法事先确定传递的位置实参的个数时,使用可变的位置参数
-
使用*定义个数可变的位置形参
-
结果为一个元祖
def fun5(*args): print(args) fun5(10) fun5(10,20,30)
输出:
(10,)
(10, 20, 30)
个数可变的关键字形参
-
定义函数时,无法事先确定传递的关键字实参的个数时,使用可变的关键字形参
-
使用**定义个数可变的关键字形参
-
结果为一个字典
def fun6(**args): print(args) fun6(a=10) fun6(a=10,b=20,c=36)
输出:
{'a': 10}
{'a': 10, 'b': 20, 'c': 36}
在函数中,可变的关键字参数只能有一个,可变的位置参数,也只能有一个
def fun2(*args,*a):
pass
以上写法错误,因为可变的位置参数,只能有一个
def fun2(**args,**a):
pass
也报错,因为个数可变的位置参数,也是只能有一个
既有个数可变的位置参数,又有个数可变的关键字形参:
def fun2(*args,**a):
pass
两种形参同时有,必须将个数可变的关键字形参放在个数可变的位置形参的后面
def fun2(*args,**a):
print(args,a)
fun2(10,11,12,a=12,b=13,c=14,d=15)
输出:
{'a': 10, 'b': 20, 'c': 36}
(10, 11, 12) {'a': 12, 'b': 13, 'c': 14, 'd': 15}
五、函数的参数总结
序号 | 参数的类型 | 函数的定义 | 函数的调用 | 备注 |
---|---|---|---|---|
1 | 位置参数 | 1 | ||
将序列中的每个元素都转换为位置参数 | 1 | 使用* | ||
2 | 关键字实参 | 1 | ||
将字典中的每个键值对都转换为关键字实参 | 1 | 使用** | ||
3 | 默认值形参 | 1 | ||
4 | 关键字形参 | 1 | 使用* | |
5 | 个数可变的位置形参 | 1 | 使用* | |
6 | 个数可变的关键字形参 | 1 | 使用** |
将参数以列表的形式传入
def fun6(a,b,c):
print('a=',a)
print('b=',b)
print('c=',c)
lst=[11,22,33]
fun6(*lst)
**想让lst中的元素对应函数的形参,一要个数对应,二要在列表名前加个 ***
参数传递的特殊情况
def func4(a,b,c,d):
print('a=',a)
print('b=',b)
print('c=',c)
print('d=',d)
位置传参:
func4(10,22,33,55)
输出:
a= 10
b= 22
c= 33
d= 55
关键字传参:
func4(a=12,b=11,c=15,d=66)
输出:
a= 12
b= 11
c= 15
d= 66
混合方式:
func4(12,15,c=18,d=19)
输出:
a= 12
b= 15
c= 18
d= 19
传参方式限制
def func8(a,b,*,c,d):
print('a=',a)
print('b=',b)
print('c=',c)
print('d=',d)
中间放颗 * ,之前的位置传参,之后的关键字传参,否则报错
print(12,17,18,d=20)
结果报错:
TypeError: func8() takes 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given
func8(12,17,c=18,d=20)
输出:
a= 12
b= 17
c= 18
d= 20
形参的顺序问题
def fun9(a,b,*,c,d,**args):
pass
def func10(*args,**args2):
pass
def func11(a,b=10,*args,**args2):
pass
上面方式都是可以的
六、变量的作用域
def fun(a,b):
c=a+b #c,就称为局部变量,因为c是在函数体内进行定义的变量,a,b为函数的形参,作用范围就是函数内部,相当于局部变量
print(c)
当在函数体外进行操作变量c时:
print(c)
会报红:Unresolved reference ‘c’
name ='杨老师' #name的作用范围为函数内部和外部都可以使用 ---全局变量
print(name)
def func2():
print(name)
func2()
杨老师
杨老师
- 在局部定义全局变量
- global
def fun3():
global age #函数内部定义的变量,局部变量,局部变量使用global声明,这个变量实际上就变成了全局变量
age =20
print(age)
fun3()
print(age)
20
20
七、递归函数
什么是递归函数
- 如果在一个函数的函数体内调用了该函数本身,这个函数就称为递归函数
递归函数的组成部分
- 递归调用与递归终止条件
递归的调用过程
- 每递归调用一次函数,都会在栈内存分配一个栈帧
- 没执行完一次函数,都会释放相应的空间
递归的优缺点
- 缺点:占用内存多,效率低下
- 优点:思路和代码简单
示例:
print('-----递归函数-----')
def fac(n):
if n == 1:
return 1
else:
return n*fac(n-1)
print(fac(6))
输出:
-----递归函数-----
720
斐波那契额数列
#斐波那契额数列
def fib(n):
if n == 1:
return 1
elif n ==2:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(7))
#输出这个数列前几位上的数字
for i in range(1,8):
print(fib(i))
输出:
13
1
1
2
3
5
8
13