目录
函数
1、函数的创建
2、函数的参数传递
3、函数的返回值
4、函数的参数定义
1)函数定义默认值参数
2)个数可变的位置参数
3)个数可变的关键字形参
4)传入多个个数可变的参数
5、函数的参数总结
1)将序列中的每个元素,都转换为位置实参;使用*序列对象
2)将字典中的每个键值对,都转换为关键字实参;使用**字典对象
3)举例
4)函数定义时,参数使用了*,在函数调用时,*之后的参数只能使用关键字实参传递
5)函数定义时的形参的顺序问题
6、变量的作用域
1)局部变量
2)全局变量
7、递归函数
8、斐波那契数列
9、总结
函数
- 执行特定的任务,和完成特定功能的一段代码;
函数的作用:
- 复用代码;
- 隐藏实现细节;
- 提高可维护性;
- 提高可读性,便于调试;
1、函数的创建
1)函数的创建,语法格式:
def 函数名([输入参数]):
函数体
[return XXX]
2、函数的调用,语法格式:
函数名([实际参数])
# 函数的创建
# 函数或类定义后,建议间隔2行
def calc(a, b):
c = a + b
return c
# 函数的调用
result = calc(10, 20)
print(result)
2、函数的参数传递
1)函数调用的参数传递
- 位置实参,根据形参对应的位置进行实参传递;
- 关键字实参,根据形参名称进行实参传递;
- 在函数调用过程中,进行参数的传递:
- 如果是不可变对象,在函数体的修改,不会影响实参的值;如整数,字符串,元组
- 如果是可变对象,在函数体的修改,会影响实参的值;如列表,字典,集合
# 函数的创建,函数或类定义后,建议间隔2行
def calc(a, b): # a,b为形式参数,简称形参,形参的位置是在函数的定义处
c = a + b
print('a=', a)
print('b=', b)
print('-------------')
return c
# 函数的调用
result1 = calc(10, 20) # 10,20为实际参数的值,简称实参,实参的位置是函数的调用处
print('result1=', result1)
result2 = calc(b=30, a=20) # 左侧的变量的名称称为 关键字参数,在定义处找到名字相同的参数赋值
print('result2=', result2)
输出结果:
a= 10
b= 20
-------------
result1= 30
a= 20
b= 30
-------------
result2= 50
函数调用的参数传递内存分析图:
def fun(arg1, arg2):
print('arg1=', arg1)
print('arg2=', arg2)
arg1 = 100
arg2.append(55)
print('--------参数修改后-----------')
print('arg1=', arg1)
print('arg2=', arg2)
n1 = 11
n2 = [22, 33, 44]
print('n1=', n1)
print('n2=', n2)
print('------------------')
fun(n1, n2) # 位置传参,arg1,arg2是函数定义处的形参,n1,n2是函数调用处的实参,即实参和形参的名称可以不一致
print('n1=', n1)
print('n2=', n2)
输出结果:
n1= 11
n2= [22, 33, 44]
------------------
arg1= 11
arg2= [22, 33, 44]
--------参数修改后-----------
arg1= 100
arg2= [22, 33, 44, 55]
n1= 11
n2= [22, 33, 44, 55]
3、函数的返回值
- 如果函数没有返回值(函数执行完毕之后,不需要给调用处提供数据),return可以省略不写;
- 函数的返回值,如果是1个,直接返回类型;
- 函数的返回值,如果是多个,返回的结果为元组;
# 1)函数没有返回值,return可以省略不写
def fun1():
print('没有返回值')
fun1()
# 2)函数的返回值,如果是1个,直接返回类型
def fun2():
return '只有1个返回值'
result = fun2()
print(result)
# 3)函数的返回值,如果是多个,返回的结果为元组。
def fun3():
return '第1个返回值', '第2个返回值'
result = fun3()
print(result)
输出结果:
没有返回值
只有1个返回值
('第1个返回值', '第2个返回值')
函数返回多个值时,结果为元组
# 函数返回多个值时,结果为元组
def fun(num):
odd = [] # 存奇数
even = [] # 存偶数
for i in num:
if i%2:
odd.append(i)
else:
even.append(i)
return odd, even
lst = [1, 2, 3, 4, 5, 6]
res = fun(lst)
print('返回多个值时,结果为元组:', res) # ([1, 3, 5], [2, 4, 6])
# 访问函数返回的多个值,元组
for i in range(len(res)):
if i == 0:
print('-------奇数列表:--------')
elif i == 1:
print('-------偶数列表:-------')
for item in res[i]:
print(item)
输出结果:
返回多个值时,结果为元组: ([1, 3, 5], [2, 4, 6])
-------奇数列表:--------
1
3
5
-------偶数列表:-------
2
4
6
4、函数的参数定义
1)函数定义默认值参数
- 函数定义时,给形参设置默认值,只有与默认值不符的时候,才需要传递实参;
# 函数定义默认值参数
def fun(a, b=10): # b为默认值参数
print('a=', a, 'b=', b)
# 函数的调用
fun(100) # 只传一个参数,b采用默认值,a= 100 b= 10
fun(20, 30) # 30将默认值10替换,a= 20 b= 30
# print函数的end为默认值参数,end='\n'
print('hello', end='\t')
print('world') # hello world
2)个数可变的位置参数
- 定义函数时,无法事先确定传递的位置实参的个数时,使用可变的位置参数;
- 使用 * 定义个数可变的位置形参;
- 结果为一个元组;
# 定义个数可变的位置参数,结果是元组
def fun(*args):
print(args)
print(args[0])
fun(10) # (10,)
fun(1, 2, 3, 4) # (1, 2, 3, 4)
输出结果:
(10,)
10
(1, 2, 3, 4)
1
3)个数可变的关键字形参
- 定义函数时,无法事先确定传递的关键字实参的个数时,使用可变的关键字形参;
- 使用 ** 定义个数可变的关键字形参;
- 结果为一个字典;
# 定义个数可变的关键字形参,结果是字典
def fun(**args):
print(args)
print(args['a']) # 1
fun(a=1) # {'a': 1}
fun(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
输出结果:
{'a': 1}
1
{'a': 1, 'b': 2, 'c': 3}
1
4)传入多个个数可变的参数
- 1)定义多个个数可变的位置参数,报错;个数可变的位置参数,只能是1个;
- 2)定义多个个数可变的关键字形参,报错;个数可变的关键字形参,只能是1个;
- 3)定义一个函数,既有个数可变的位置参数,又有个数可变的关键字形参:
- 3.1)个数可变的关键字形参在前,报错;
- 3.2)个数可变的位置参数在前,可运行通过;
- 即个数可变的位置参数,要在个数可变的关键字形参之前;
# 1)定义多个个数可变的位置参数,报错
def fun(*args, *a): # 报错,SyntaxError: invalid syntax
pass
# 2)定义多个个数可变的关键字形参,报错
def fun(**args, **a): # 报错,SyntaxError: invalid syntax
pass
# 3)定义一个个数可变的位置参数,一个个数可变的关键字形参
# 3.1)个数可变的关键字形参在前,报错
def fun(**args, *a): # 报错,SyntaxError: invalid syntax
pass
# 3.2)个数可变的位置参数在前,可运行通过
def fun(*args, **a):
pass
5、函数的参数总结
1)将序列中的每个元素,都转换为位置实参;使用*序列对象
# 将序列中的每个元素,都转换为位置实参
def fun(a, b, c):
print('a=', a)
print('b=', b)
print('c=', c)
print('----------------')
# 函数的调用
fun(1, 2, 3) # 函数调用时的参数传递,称为位置传参
lst = [4, 5, 6]
# fun(lst) # 直接传列表,会报错,TypeError: fun() missing 2 required positional arguments: 'b' and 'c'
fun(*lst) # 在函数调用时,将列表的每个元素,都转换为位置实参传入
输出结果:
a= 1
b= 2
c= 3
----------------
a= 4
b= 5
c= 6
----------------
2)将字典中的每个键值对,都转换为关键字实参;使用**字典对象
# 将字典中的每个键值对,都转换为关键字实参
def fun(a, b, c):
print('a=', a)
print('b=', b)
print('c=', c)
print('----------------')
# 函数的调用
fun(a=10, b=20, c=30) # 关键字实参
dic = {'a': 40, 'b': 50, 'c': 60}
# fun(dic) # 直接传入字典,会报错,TypeError: fun() missing 2 required positional arguments: 'b' and 'c'
fun(**dic) # 在函数调用时,将字典中的键值对,都转换为关键字实参
输出结果:
a= 10
b= 20
c= 30
----------------
a= 40
b= 50
c= 60
----------------
3)举例
# 函数定义和调用
def fun1(a, b=3): # 函数定义默认值参数
print(a,b)
def fun2(*args): # 个数可变的位置参数
print(args)
def fun3(**args): # 个数可变的关键字形参
print(args)
fun1(1, 2)
fun2(10, 20, 30)
fun3(a=100, b=200, c=300, d=400)
输出结果:
1 2
(10, 20, 30)
{'a': 100, 'b': 200, 'c': 300, 'd': 400}
# 函数定义和调用
def fun4(a, b, c, d):
print('a', a)
print('b', b)
print('c', c)
print('d', d)
print('--------------')
# 调用fun4()函数
fun4(1, 2, 3, 4) # 位置实参传递
fun4(a=1, b=2, c=3, d=4) # 关键字实参传递
fun4(1, 2, c=3, d=4) # 前2个参数,是位置实参传递;而c,d是关键字实参传递
4)函数定义时,参数使用了*,在函数调用时,*之后的参数只能使用关键字实参传递
需求:c,d只能使用关键字实参传递
# 函数定义和调用
def fun4(a, b, *, c, d): # *后面的形参,在函数调用时,c,d只能使用关键字实参传递
print('a', a)
print('b', b)
print('c', c)
print('d', d)
print('--------------')
# 调用fun4()函数
# fun4(1, 2, 3, 4) # 位置实参传递,会报错,TypeError: fun4() takes 2 positional arguments but 4 were given
fun4(a=1, b=2, c=3, d=4) # 关键字实参传递,运行成功
fun4(1, 2, c=3, d=4) # 前2个参数,是位置实参传递;而c,d是关键字实参传递,运行成功
5)函数定义时的形参的顺序问题
# 函数定义时的形参的顺序问题
def fun5(a, b, *, c, d, **args):
pass
def fun6(*args1, **args2):
pass
def fun7(a, b=10, *args1, **args2):
pass
6、变量的作用域
- 程序代码能访问该变量的区域;
- 根据变量的有效范围,可分为局部变量,全局变量;
- 局部变量,在函数内定义并使用的变量,只在函数内部有效,局部变量使用global声明,这个变量就会变为全局变量;
- 全局变量,函数体外定义的变量,可作用于函数内外;
1)局部变量
# 变量的作用域
# 局部变量
def fun(a, b): # a,b为函数的形参,作用范围也是函数内部,相当于局部变量;
c = a + b # c是局部变量,在函数体内定义的变量;
print(c)
# print(c) # 报错,NameError: name 'c' is not defined
# print(a) # 报错,NameError: name 'a' is not defined
2)全局变量
# 全局变量
name = '张老师' # name是全局变量,作用范围为函数内部和外部都可以使用
print(name)
def fun():
print(name)
fun()
输出结果:
张老师
张老师
函数体内的局部变量,转为全局变量,使用global
# 局部变量转为全局变量
def fun():
global age # 局部变量转为全局变量
age = 10
print(age)
fun()
print(age) # age为全局变量,函数体外面也可以使用了
7、递归函数
- 如果一个函数的函数体内调用了该函数本身,这个函数就是递归函数;
- 递归的组成部分:递归调用,递归终止条件;
- 递归的调用过程:
- 每递归调用一次函数,都会在栈内存分配一个栈帧;
- 每执行完一次函数,都会释放相应的空间;
- 递归的优缺点:
- 缺点:占用内存多,效率低下;
- 优点:思路和代码简单;
使用递归来计算阶乘
fac(6) | 6! = 6x5x4x3x2x1 |
6*fac(5) | 6! = 6x5! |
5*fac(4)) | 5! = 5x4! |
4*fac(3) | 4! = 4x3! |
3*fac(2) | 3! = 3x2! |
2*fac(1) | 2! = 2x1! |
fac(1) | 1! = 1 |
# 使用递归来计算阶乘
def fac(n):
if n == 1:
return 1
else:
return n * fac(n-1)
res = fac(6)
print(res) # 720
8、斐波那契数列
数列:0、1、1、2、3、5、8、13、21、34、……
斐波那契数列,以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
# 斐波那契数列
def fib(n):
if n == 1:
return 0
elif n == 2:
return 1
else:
return fib(n-1) + fib(n-2)
# 输出斐波那契数列第7位上的数字
res = fib(7)
print(res)
# 输出斐波那契数列前7位上的数字
lst = []
for i in range(1, 8, 1):
lst.append(fib(i))
print(lst)
输出结果:
8
[0, 1, 1, 2, 3, 5, 8]
9、总结