函数的基本使用
函数的定义
定义函数的格式:
def 函数名():
代码
函数名称
要能够表达函数所封装的代码的功能,方便后续调用函数名称
的命名要符合其命名规则,即,由数字,字母和下划线组成,且不能以数字开头
,更不能与关键字重名
可以在调用函数的末尾加上return
来返回一个数值,如下
def 函数名():
代码
return #返回值
- 函数中可以有多个
return
,但是只要执行到return
,则完成调用
当然也可以在函数中添加一些参数
- 参数之间使用逗号隔开
def 函数名(形参):
代码
#根据是否需要返回值,来决定是否添加return
函数的调用
函数定义完成后必须主动调用,调用函数仅需函数名
进行调用,但是因为代码是从上往下执行,因此,在调用函数之前必须先定义函数
函数的参数
我们已经了解了函数的基础定义及调用
示例
def add()
result = 1 + 2
print(f"1+2的结果时{result}")
但是这样我们仅能通过调用add
输出1+2的结果,并不能高效的重复使用代码
因此我们可以在定义函数时加入一些形式参数,表示函数声明即将使用的参数,在调用函数进行计算时,接收外部提供的数据
示例
def add(num1,num2):
result = num1 +num2
print(f"{num1}+{num2}={result}")
add(1,2)
#输出结果
1+2=3
需要注意的是,在传入参数时默认按照输入 顺序
,分别赋值给形参,且实际参数的数量要与形式参数相同
示例
def add(num1,num2):
result = num1 +num2
print(f"{num1}+{num2}={result}")
add(1)#会报错:TypeError: add() missing 1 required positional argument: 'num2'
默认值参数
我们可以在定义函数时,给参数赋予一个默认值
示例
def add(num1=0,num2=0):
result = num1 +num2
print(f"{num1}+{num2}={result}")
add()#此时我们如果直接调用add函数
0+0=0#输出结果
add(1)#也可以仅输入一个参数
1+0=1
注意:默认参数后面不可跟随非默认形参
示例
def add(num1=0,num2):
def add(num1,num2=0,num3):
不可按照上述形式定义函数
关键字参数
上面我们提到,我们在调用函数时默认所输入的实参是会按照 顺序
传入形参
如下所示
def add(num1,num2):
result = num1 +num2
print(f"{num1}+{num2}={result}")
add(1,2)
#输出结果
1+2=3
但我们可以在传入参数时加入关键字,来改变这一顺序
如下所示
def add(num1,num2):
result = num1 +num2
print(f"{num1}+{num2}={result}")
add(num1=2,num2=1)
#输出结果
2+1=3
注意,位置实参不可位于关键字参数后
add(num1=2,1)
不可以按照上述形式调用函数
命名关键字参数
使用“*”占位,使其后面必须使用关键字传参
在上面的示例中(未定义默认值参数),我们可以有如下几种传参的形式
add(1,2)
add(1,num2=2)
add(num1=1,num2=2)
但是如果我们在定义函数时,在形参前加上“*”,则 “*”之后的参数在传入时必须使用关键词参数进行传参
示例
def add(*,num1, num2 ):
result = num1 + num2
print(f"{num1}+{num2}={result}")
add(num1=1,num2=2)
可变参数
可变参数允许函数接受任意数量的参数,并将其作为元组处理,这样可以方便地处理不确定数量的输入
可变参数有两种形式:*args
和kwargs
**
*args
表示接受任意数量的位置参数。它会将传递给函数的所有位置参数打包成一个元组。在函数内部,可以通过对该元组进行遍历或索引来访问每个参数。- 注意,如果
*args
前面有其它参数,则在传入时会先传给其它参数,剩余的位置参数才会传入*args
- 同时,不允许多个
“*”
形参存在
示例
def sum_numbers(*args):
total = 0
for num in args:
total += num
return total
print(sum_numbers(1, 2, 3)) # 输出6
print(sum_numbers(4, 5, 6, 7, 8)) # 输出30
def add(num1, num2 ,*args):
result = num1 + num2
print(f"{num1}+{num2}={result}")
print(*args)
add(1,2,123)
#输出结果
1+2=3
123
**kwargs
表示接受任意数量的关键字参数。它会将传递给函数的所有关键字参数打包成一个字典。在函数内部,可以通过字典的键值对来访问每个参数的名称和值- 同样如果前面有其它关键字参数,则会先传给其它关键字参数
def print_info(*,a,**kwargs):
print(a)
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(a=1,name="甲", age=25)
#输出结果
1
name: 甲
age: 25
注意:函数定义时,二者同时存在,一定需要将*args放在 kwargs之前
在调用函数使用可变参数时,可以直接传递多个参数或关键字参数,也可以通过解包已有的字符串,元组,列表,集合或字典来传递参数
- 使用*args解包列表:
def print_numbers(*args):
for num in args:
print(num)
numbers = [1, 2, 3, 4, 5]
print_numbers(*numbers)
输出结果:
1
2
3
4
5
- 使用**kwargs解包字典:
def print_info(name, age):
print(f"Name: {name}")
print(f"Age: {age}")
info = {"name": "乙", "age": 25}
print_info(**info)
输出结果:
Name: 乙
Age: 25
注意:在解包字典时,{key}要与形参对应
函数的返回值
函数的返回值是指在函数执行完毕后,将结果返回给调用者。使用关键字return来指定函数的返回值,调用者可以使用变量来接收函数的返回值
- 返回单个值:
def add_numbers(a, b):
return a + b #接受两个参数并返回它们的和
result = add_numbers(1, 2) #将返回值赋给变量result
print(result) #输出3
- 返回多个值:
def get_name_and_age():
name = "丙"
age = 25
return name, age #返回了一个包含姓名和年龄的元组
person_info = get_name_and_age() #将返回值赋给变量person_info
name, age = person_info #解包(即将元组的每个元素分别赋给不同的变量
print(name) # 输出"丙"
print(age) # 输出25
空值(None)
def do_something():
#执行一些操作,但没有具体的返回值
pass
result = do_something()
print(result) # 输出None
- 函数没有明确的返回值,或者只需要执行一些操作而不需要返回结果,可以主动使用return来返回None
- None在if语句中与False等效,可参与if判断
- 声明一些无内容的变量
注意:在函数中遇到return语句后,函数将立即结束执行,并将返回值传递给调用者。因此,如果有多个return语句,只会执行到第一个遇到的return语句并返回对应的值
函数的嵌套调用
函数嵌套可以让代码组织成更小、更可读和可重复使用的块,同时还提供了一种封装变量和功能的方式。在外层函数中调用到内层函数,会将内层函数全部执行完毕后继续执行外层函数,通过合理利用函数嵌套,可以编写出更模块化和灵活的代码
示例
def print_line1(char, num):
print(char * num)
def print_line2(char, num):
row = 0
while row < 5:
print_line1(char, num)
row += 1
print_line2('*', 10)
#输出结果
**********
**********
**********
**********
**********
- 内部函数可以访问外部函数的变量和参数
- 内部函数可以修改外部函数的可变对象
def out_num():
numbers = [1, 2, 3]
def add_number(num):
numbers.append(num)
add_number(4)
print(numbers) # 输出[1, 2, 3, 4]
out_num()
递归
- 递归是指在一个函数内部调用自身的过程
- 编程中,递归通常用于解决可以被分解为相同问题的子问题的情况
- 使用递归时必须确保存在一个终止条件,以避免无限递归导致栈溢出错误
递归经典案例
汉诺塔:
将A柱的圆盘移至C柱,一次仅能移动一个圆盘,且较大的圆盘必须在较小的圆盘下方(A柱的圆盘的大小从上到下依次增大)
原理:将总计n层的圆盘的从上至下的(n-1)层看成整体,这就将一个N层的圆盘简化成两层,先将上面一层移至B柱,将最下面的一层即第n层最先移至 C柱,再将上面一层(看作整体的(n-1)层)从B柱移至C柱
def han_nuo_ta (n,A,B,C):
if n == 1:
print(A,"->",C) #如果只有一层,直接从A柱移至C柱
return
han_nuo_ta(n-1,A,C,B) #内层函数的形参作为实参传入外层函数,将外层的B,C调换位置,改变输出结果,将(n-1)层移至B柱
han_nuo_ta(1,A,B,C) #将A柱最底层移至C柱
han_nuo_ta(n-1,B,A,C) #内层函数再次修改传入外层函数的实参,调换A.B的位置,改变输出结果,将B柱的(n-1)层移至C柱
han_nuo_ta(3,"A","B","C")
输出结果
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C