一、函数概述
(一)函数的定义
函数是组织好的、实现单一功能或相关联功能的代码段。以“函数名()”形式调用。
1.未使用函数的程序
for i in range(5):
for i in range(5):
print('*',end=" ")
print()
2.使用函数的程序
def print_triangle(lenth):
for i in range(lenth):
for i in range(lenth):
print('*'," ")
print()
print_triangle(5)
(二)函数式编程的优点:
- 将程序模块化,既减少了冗余代码,又让程序结构更为清晰
- 提高开发人员的编程效率
- 方便后期的维护与扩展
二、函数的定义和调用
(一)定义函数:def关键字
语法格式:
def 函数名 ([参数列表]): #函数名是函数的唯一标识;参数列表可以 包含一个或多个参数
函数体 #具体代码
return #return函数的结束;若没有返回值,可以省略
计算两数之和的函数:
#无参函数 def add(): result = 11 + 22 print(result) #有参函数 def add_modify(a, b): result = a + b print(result)
(二)调用函数
1.函数要被程序调用时才会执行。
语法格式:
函数名([参数列表])
add() add_modify(10, 20)
2. 函数内部可以调用其他函数(函数的嵌套调用)
def add_modify(a, b):
result = a + b
add()
print(result)
add_modify(10, 20)
函数的嵌套(外层函数、内层函数)
def add_modify(a, b): result = a + b print(result) def test(): print("我是内层函数") add_modify(10, 20)
注意:
- 函数外部无法直接调用内层函数
- 只能通过外层函数间接调用内层函数
三、函数参数的传递
函数参数的定义:定义函数是设置的参数称为形式参数(形参),调用函数时传入的参数称为实际参数(实参)。
(一)位置参数的传递
调用函数时将实参按照相应的位置依次传递给形参
def get_max(a, b):
if a > b:
print(a,"是较大的值!")
else:
print(b,"是较大的值!")
get_max(8, 5)
(二)关键字参数的传递
1.通过“形参=实参”的歌声将实参与形参相关联,将实参按照相应的关键字传递给形参
def connect(ip, port):
print(f"设备{ip}:{port}连接!")
connect(ip="127.0.0.1", port=8080)
2.使用符号“/”来限定部分形参只接收采用位置传递方式的实参
def func(a, b, /, c):
print(a, b, c)
#错误的调用方式
#func(a=10,20,30)
#func(10,b=20,30)
#正确的调用方式
#func(10,20,c=30)
(三)默认参数的传递
定义函数时可以指定形参的默认值,在被调用时可以选择是否给带有默认值的形参传值
#定义:
def connect(ip, port=8080):
print(f"设备{ip}:{port}连接!")
#调用:
connect(ip="127.0.0.1")
connect(ip="127.0.0.1", port=3306)
#结果:
设备127.0.0.1:8080连接!
设备127.0.0.1:3306连接!
(四)参数的打包与解包
1.打包
(1)“*”——接收以元组形式打包的多个值
#定义:
def test(*args):
print(args)
#调用:
test(11, 22, 33, 44, 55)
#结果:
(11, 22, 33, 44, 55)
(2)“**”——接收以字典形式打包的多个值
#定义:
def test(**kwargs):
print(kwargs)
#调用:
test(a=11, b=22, c=33, d=44, e=55)
#结果:
{'a': 11, 'b': 22, 'c': 33, 'd': 44, 'e': 55}
注意:
- 函数中添加“*”或“**”的形参可以符合命名规范的任意名称,但是建议使用*args和**kwargs
- 若函数没有接收到任何数据,参数*args*和*kwargs为空,即为空元组或空字典
2.解包
(1)实参是元组,可以使用“*”拆分成多个值,按位置参数传给形参
#定义:
def test(a, b, c, d, e):
print(a, b, c, d, e)
#调用:
nums = (11, 22, 33, 44, 55)
test(*nums)
#结果:
11 22 33 44 55
(2)实参是字典,可以使用“**”拆分成多个键值对,按关键字参数传给形参
#定义:
def test(a, b, c, d, e):
print(a, b, c, d, e)
#调用:
nums = {"a":11, "b":22, "c":33, "d":44, "e":55}
test(**nums)
#结果:
{'a': 11, 'b': 22, 'c': 33 , 'd': 44, 'e': 55}
(五)混合传递
参数传递的方式在定义函数或调用函数时可以混合使用
使用规则:
- 按位置参数传递的方式
- 按关键字参数传递的方式
- 按默认参数传递的方式
- 按打包传递的方式
在定义函数时的注意事项:
- 带有默认值的参数必须位于普通参数之后
- 带有“*”标识的参数必须位于带有默认值的参数之后
- 带有“**”标识的参数必须位于带有“*”标识的参数之后
#定义:
def test(a, b, c=33, *args, **kwargs):
print(a, b, c, args, kwargs)
#调用:
test(1, 2)
test(1, 2, 3)
test(1, 2, 3, 4)
test(1, 2, 3, 4, e=5)
#结果:
1 2 33 () {}
1 2 3 () {}
1 2 3 (4,) {}
1 2 3 (4,) {'e': 5}
四、函数的返回值
(一)函数中的return语句会在函数结束时将数据返回给程序,同时让程序回到被调用的位置继续执行
#定义: def filter_sensitive_words(words): if "山寨" in words: new_words = words.replace("山寨", "**") return new_words #调用: result = filter_sensitive_words("这个手机是山寨版吧!") print(result) #结果: 这个手机是**版吧!
(二)如果函数使用return语句返回多个值,那么这些值将会被保存到元组中
#定义: def move(x, y, step): nx = x + step ny = y - step return nx, ny # 使用return语句返回多个值 #调用: result = move(100, 100, 60) print(result) #结果: (160, 40)
五、变量作用域
(一)局部变量和全局变量
1.局部变量
注意:
- 函数内部定义的变量,只能在函数内部被使用
- 函数执行结束之后局部变量会被释放,此时无法再进行访问
def test_one(): number = 10 # 局部变量 print(number) # 函数内部访问局部变量 test_one() print(number) # 函数外部访问局部变量
不同函数内部可以包含同名的局部变量:
def test_one(): number = 10 print(number) # 访问test_one()函数的局部变量number def test_two(): number = 20 print(number) # 访问test_two()函数的局部变量number test_one() test_two() #结果: 10 20
2.全局变量
全局变量可以在整个程序的范围内起作用,不受函数范围的影响
number = 10 # 全局变量 def test_one(): print(number) # 函数内部访问全局变量 test_one() print(number) # 函数外部访问全局变量 #结果: 10 10
全局变量在函数内部只能被访问,而无法直接修改
# 定义全局变量 number = 10 def test_one(): print(number) number += 1 test_one() print(number)
3.补充
LEGB
局部作用域(L) 例如,局部变量和形参生效的区域 嵌套作用域(E) 例如,嵌套定义的函数中外层函数声明的变量生效的区域 全局作用域(G) 例如,全局变量生效的区域 内置作用域(B) 例如,内置模块声明的变量生效的区域 按照L-E-G-B的顺序依次搜索,若四张区域中无法找到变量,程序抛出异常
(二) global和nonlocal关键字
1.global关键字
使用global关键字可以将局部变量声明为全局变量
number = 10 # 定义全局变量 def test_one(): global number # 使用global声明变量number为全局变量 number += 1 print(number) test_one() print(number)
2.nonlocal关键字
使用nonlocal关键字可以在局部作用域中修改嵌套作用域中定义的变量
def test(): number = 10 def test_in(): nonlocal number number = 20 test_in() print(number) test()
六、特殊形式的函数
(一)递归函数
定义递归函数需要满足的条件:
- 递归公式是求解原问题或相似的子问题的结构
- 边界条件是最小化的子问题,也是递归终止的条件
递归函数的执行分为两个阶段:
- 递推:递归本次的执行都基于上一次的运算结果
- 回溯:遇到终止条件时,则沿着递推往回一级一级的把值返回来
定义递归函数的格式:
def 函数名([参数列表])
if 边界条件:
return 结果
else:
return 递归公式
举例:
def func(num): if num == 1: return 1 else: return num * func(num - 1) num = int(input("请输入一个整数:")) result = func(num) print("5!=%d"%result)
(二)匿名函数
使用lambda关键字定义匿名函数,语法格式:
lambda <形式参数列表>:<表达式>
匿名函数与普通函数的区别:
- 普通函数在定义是有名称,而匿名函数没有名称
- 普通函数的函数体包含多条语句,而匿名函数的函数体只能是一个表达式
- 普通函数可以实现的功能比较复杂,匿名函数可以实现的功能比较简单
- 普通函数能被其他程序使用,而匿名函数不能被其他程序使用