编程中的函数,是一段可以被重复使用的代码片段,使用函数能够减少冗余的代码。比如:
# 1.求1-100的和
sum = 0
for i in range(1, 101):
sum += i
print(f'sum = {sum}') # 5050
# 2.求300-400的和
sum = 0
for i in range(300, 401):
sum += i
print(f'sum = {sum}') # 35350
# 3.求1-1000的和
sum = 0
for i in range(1, 1001):
sum += i
print(f'sum = {sum}') # 500500
上述几组代码的处理逻辑都是类似的,这种情况就可以把重复的代码提取出来,封装成一个函数,需要求和时直接调用该函数即可。比如:
def calcSum(begin, end):
theSum = 0
for i in range(begin, end + 1):
theSum += i
return theSum
print(calcSum(1, 100)) # 5050
print(calcSum(300, 400)) # 35350
print(calcSum(1, 1000)) # 500500
这时就可以明显看到,重复的代码已经被消除了。
函数的定义格式
多个返回值
此外,Python中的一个函数可以有一个或多个返回值,当有多个值需要返回时,使用,
来分隔这多个返回值。比如:
def getSumAndDif(x, y):
return x + y, x - y
a = 10
b = 20
sum, dif = getSumAndDif(a, b)
print(f'{a} + {b} = {sum}, {a} - {b} = {dif}')
Python中一个函数能返回多个值,本质就是多元赋值所支持的。如果函数调用者只想关注该函数的部分返回值,那么可以使用_
来忽略不想要的返回值。比如:
_, dif = getSumAndDif(1, 2)
print(dif)
函数的调用格式
传参方式
调用函数时可以根据函数的形参列表,将实参列表中的实参按照顺序依次传给对应的形参。比如:
def Dif(x, y):
return x - y
print(Dif(10, 20)) # -10
此外,也可以通过关键字参数来调整传参的顺序,显式指定当前实参传递给哪个形参。比如:
print(Dif(y=10, x=20)) # 10
链式调用指的是把一个函数的返回值,作为调用另一个函数时的参数进行传入。
例如,下面代码中调用print函数时,将Add函数的返回值作为了参数。
def Add(x, y):
return x + y
print(Add(10, 20)) # 30
嵌套调用指的是在一个函数的函数体内部,调用了其他函数。
例如,下面代码中的test函数内部调用了print函数。
def test():
print('do something...')
print('do something...')
print('do something...')
test()
递归掉是嵌套调用的一种特殊情况,即在一个函数内部嵌套调用自己。
例如,下面的factor函数可以用来求一个数的阶乘。
def factor(n):
if n == 1:
return 1
return n * factor(n - 1)
在factor函数内部又嵌套调用了factor函数,这就是递归调用。
递归调用与返回的过程
以求3的阶乘为例,factor函数的递归调用与返回过程如下图:
即一层层进行递归调用,当满足递归结束条件时再逐层进行返回。
递归的两个条件
需要注意的是,递归必须满足以下两个条件:
以上述的factor函数为例,其递归结束条件就是if语句中的n == 1
,当n为1的时候递归就结束了,而每次递归调用factor函数时传入的参数都是n - 1
,因此每进行一次递归n的值都会逐渐逼近1。如果不能满足这两个条件,就会出现无限递归,最终导致栈溢出(Stack Overflow)。
说明一下: 函数调用时会在函数调用栈中记录每一层函数调用的信息,但函数调用栈的空间不是无限大的,如果函数调用层数太多,就会超出栈的最大范围,进而导致栈溢出。
递归的优缺点
递归的优点:
递归的缺点:
可以给函数的形参指定默认值,在调用函数时可以不必给带有默认值的形参传参,此时该形参将会使用我们指定的默认值。
例如,下面调用Add函数时如果不传入第二个参数,则默认返回第一个参数与1的和,如果调用Add函数时传入了第二个参数,则返回这两个参数的和。
def Add(data, val=1):
return data + val
print(Add(10)) # 11
print(Add(10, 2)) # 12
注意: 在定义函数时,带有默认值的参数需要放到没有默认值的参数后面。
如果想让函数接收任意个数的参数,可以在函数定义的形参名称前面添加*
,这时函数将接收一个参数元组,并且我们可以以形参名[下标]
的方式相应地访问传入的各个实参。比如:
def Func(*arg):
n = len(arg)
for i in range(n):
print(arg[i])
Func(1)
Func(1, 2, 3)
说明一下: 元组是Python中的一个数据类型,本文先不做介绍。
变量的作用域
每一个变量都有自己的作用域:
注意事项:
函数中操作全局变量
需要注意的是,如果在函数内部尝试访问的变量在局部不存在,就会尝试去全局域中查找。比如:
x = 10
def Func():
print(f'x = {x}')
Func() # x = 10
但如果想要在函数内部修改全局变量的值,就需要使用global关键字进行声明。比如:
x = 10
def Func():
global x # 声明
x = 20
print(f'函数内部: x = {x}') # x = 20
Func()
print(f'函数外部: x = {x}') # x = 20
如果在函数内部修改全局变量时没有用global关键字进行声明,那么此时Python解释器会认为你是想在函数内部创建一个同名的局部变量,此时你的修改操作就不会影响到对应的全局变量。比如:
x = 10
def Func():
# global x
x = 20
print(f'函数内部: x = {x}') # x = 20
Func()
print(f'函数外部: x = {x}') # x = 10
if、while、for中的变量
Python中的if、while、for等语句块不会影响变量的作用域,因此在这些语句块中定义的变量,也能在外面正常使用。比如:
for i in range(10):
print(f'函数内部: i = {i}')
print(f'函数外部: i = {i}') # i = 9