1.1 函数的引言
函数是组织好的、实现单一功能或相关联功能的代码段。
可以将函数视为一段有名字的代码,这类代码可以在需要的地方以“函数名()”的形式调用。
使用函数来编程可使程序模块化,即减少了冗余代码,又让程序结构更为清晰,方便后期的管理和维护。
1.2 函数的定义和调用
print()和input()函数都是Python的内置函数,这些函数由Python定义。
我们也可以根据自己的需求定义函数。
函数在定义完成之后不会立刻执行,直到被程序调用时才会执行。
# 定义函数的语法格式:
def 函数名([参数列表]):
['''文档字符串''']
函数体
[return语句]
# 调用函数的语法格式:
函数名([参数列表])
def关键字: 函数的开始标志
函数名: 函数的唯一标识,遵循标识符的命名规则
参数列表: 负责接收传入函数中的数据,可以包含一个或多个参数,也可以为空
冒号: 函数体的开始标志
文档字符串: 由一对三引号包裹的、用于说明函数功能的字符串,可以省略
函数体: 实现函数功能的具体代码
return语句: 返回函数的处理结果给调用方,是函数结束的标志;若函数没有返回值,可以省略。
案例:定义并调用一个计算 任意2个数之和 的函数
def add(a,b):
sum = a + b
print(sum)
add(1,2)
案例:函数的嵌套调用
# 函数的内部也可以调用其他函数
def add_():
sum = 1 + 1
print(sum)
def add(a,b):
sum = a + b
add_()
print(sum)
add(1,2)
案例:函数的嵌套定义
# 函数在定义时可以在其内部嵌套定义另外一个函数
# 嵌套的函数称为外层函数 被嵌套的函数称为内层函数
# 函数外部无法直接调用内层函数 只能在外层函数中调用内层函数
def add(a,b):
sum = a + b
print(sum)
def string(): # 在函数中定义函数string()
print('我是内层函数')
string() # 在函数中调用函数string()
add(1,2)
1.3 函数的参数传递
形式参数:定义函数时设置的参数
实际参数:调用函数时传入的参数
函数的参数传递指将实际参数传递给形式参数的过程。
(1)位置参数的传递
案例:定义并调用一个获取2个数之间最大值的函数 max
# 函数在被调用时会将实参按照相应的位置依次传递给形参 即将第1个实参传递给第1个形参,以此类推。
def max(a,b):
if a > b:
print(a,'是较大的值')
else:
print(b,'是较大的值')
max(1,2)
(2)关键字参数的传递
Python使用符号 “/” 来限定部分形参只接收采用位置参数传递方式的实参
# 指明前面的参数 a, b 均为仅限位置形参
def function(a,b,/,c):
print(a,b,c)
案例:定义并调用一个连接设备的函数 connect
# 若参数数量较多,可以使用关键字参数的方式传递
# 通过“形参=实参”的格式将实参按照相应的关键字传递给形参。
def connect(ip,port):
print(f'设备{ip}:{port}连接!')
connect(ip='127.0.0.1',port=8080)
(3)默认参数的传递
案例:定义一个连接具有指定端口号设备的函数
# 函数在定义时可以指定形参的默认值
# 函数在被调用时若没有给带有默认值的形参传值,则直接使用该形参的默认值。
def connect(ip,port=8080):
print(f'设备{ip}:{port}连接!')
connect(ip='127.0.0.1')
connect(ip='127.0.0.1',port=3030)
(4)参数的打包与解包
函数支持将实参以 打包 和 解包 的形式传递给形参。
打包:
如果函数在定义时无法确定需要接受多少个数据,那么可以在定义函数时为形参添加 “*” 或 “**” 。
若添加的是“*”: 可以接收以 元组 形式打包的多个值
若添加的是“**”:可以接收以 字典 形式打包的多个值
虽然函数中添加“*”或“**”的形参可以是符合命名规范的任意名称,但建议使用*args和**kwargs。
若函数没有接收到任何数据,参数*args和**kwargs为空,即为空元组或空字典。
解包:
如果函数在调用时接收的实参是元组类型的数据,那么可以使用“*”将元组拆分成多个值,并将每个值按照位置
参数传递的方式赋值为形参
如果函数在调用时接收的实参是字典类型的数据,那么可以使用“**”将字典拆分成多个键值对,并将每个值按
照关键字参数传递的方式赋值给与键名对应的形参
案例:定义一个形参为 * args 的函数 test
def test(*args):
print(args)
test(1,2,3,4,5)
案例:定义一个形参为 * * kwargs 的函数 test
def test(**kwargs):
print(kwargs)
test(a=1,b=2,c=3,d=4,e=5)
案例:定义一个带有 5 个形参的函数 test
def test(a,b,c,d,e):
print(a,b,c,d,e)
nums = (1,2,3,4,5)
test(*nums) # 元组被解包成多个值
nums = {'a':1,'b':2,'c':3,'d':4,'e':5}
test(**nums) # 字典被解包成多个值
(5)混合传递
前面介绍的参数传递方式在定义函数或调用函数时可以混合使用,但是需要遵守优先级规则。
(高 --->低):位置参数传参 >关键字参数传参>默认参数传参>打包传参
在定义函数时,带有默认值的参数必须位于普通参数(不带默认值或标识的参数)之后,带有“*”标识的参数
必须位于带有默认值的参数之后,带有“**”标识的参数必须位于带有“*”标识的参数之后。
案例:定义一个混合了多种形式的参数的函数 test
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.4 函数的返回值
函数中的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 # 返回游戏角色目前所处位置的x,y坐标
result = move(10,10,5)
print(result)
1.5 函数的变量作用域
变量并非在程序的任意位置都可以被访问,其访问权限取决于变量定义的位置。
变量的有效范围称为该变量的作用域。
根据作用域的不同,变量可以分为:局部变量 和 全局变量
LEGB原则:程序中搜索变量时所遵循的原则
L(Local):局部作用域。 如:局部变量和形参生效的区域
E(Enclosing):嵌套作用域 如:嵌套定义的函数中外层函数声明的变量生效的区域
G(Global):全局作用域 如:全局变量生效的区域
B(Built-in):内置作用域 如:内置模块声明的变量生效的区域
Python在搜索变量的时候会依次在此4种区域中搜索变量。
若搜索到变量则终止搜索,使用搜索到的变量。
若搜索完仍然无法找到变量,程序将抛出异常。
(1)局部变量
案例:在test_1函数中定义一个局部变量 num,分别在该函数内外访问 num
# 局部变量:在函数内部定义的变量
# 只能在函数内部使用 函数执行结束之后会被释放,此时无法进行访问
def test_1():
num = 1
print(num)
test_1()
print(num)
案例:验证不同函数内部可以包含同名的局部变量
# 不同函数内部可以包含同名的局部变量,类似于不同目录下同名文件的关系 相互独立 不受影响
def test_1():
num = 1
print(num) # 访问test_1函数的局部变量num
def test_2():
num = 2
print(num) # 访问test_2函数的局部变量num
test_1()
test_2()
(2)全局变量
案例:在 test 函数外定义一个全局变量 num,分别在该函数内外访问全局变量 num
# 全局变量:可以在整个程序的范围内起作用,不会受函数范围的影响
# 在函数内部只能被访问,而无法直接修改
num = 1
def test_1():
print(num)
test_1()
print(num)
# 函数内部的变量 num 视为局部变量,而在执行“num += 1”这段代码之前并未声明局部变量 num
num = 1
def test_1():
print(num)
num += 1
test_1()
print(num)
1.6 函数的两大重要关键字
函数内部无法直接修改全局变量或在嵌套函数的外层函数声明的变量。
可以使用 global关键字 或 nonlocal关键字 修饰变量以间接修改以上变量。
(1)global 关键字
# global关键字 可以将局部变量声明为全局变量
# 语法格式:
global 变量
案例:在下列代码中,将局部变量 num 修改为全局变量
num = 1 # 定义全局变量
def test_1():
global num # 声明局部变量 num 为全局变量
num += 1
print(num)
test_1()
print(num)
(2)nonlocal 关键字
# nonlocal关键字 可以在局部作用域中修改嵌套作用域中声明的变量
# 语法格式:
nonlocal 变量
案例:在下列代码中,修改嵌套作用域中声明的变量 num
def test():
num = 1
def test_in():
nonlocal num
num = 2
test_in()
print(num)
test()
案例:编写函数输出100以内所有整教按照角谷猜想经多少次可变为1。
# 角谷猜想 :一个正整救n,若为偶教,则变为n/2,若为奇数,则乘3加1(即3n+1)
# 不新重复这样的运算,经过有限步后,必然会得到1
def guess(number):
i = 0 # 统计变换的次数
original_number = number # 记录最初的 number
while number != 1:
if number % 2 == 0: # number 为偶数
number = number / 2
else: # number 为奇数
number = number * 3 + 1
i += 1
print(f'{original_number}经过{i}次变换后回到1')
num = int(input('请输入一个大于1的正整数:'))
guess(num)
案例:利用函数实现具有显示饮品信息、计算销售总额等功能的程序
# 顾客选择想要的饮品,通过投币或扫码的方式支付,支付成功后从出货口取出饮品
def show_goods():
goods = {
'1': {'name': '可乐', 'price': 3.0},
'2': {'name': '雪碧', 'price': 2.5},
'3': {'name': '矿泉水', 'price': 1.5},
'4': {'name': '果汁', 'price': 4.0}
}
print("饮品信息:")
for key, value in goods.items():
print(f"{key}. {value['name']} - ¥{value['price']}")
def total():
goods = {
'1': {'name': '可乐', 'price': 3.0},
'2': {'name': '雪碧', 'price': 2.5},
'3': {'name': '矿泉水', 'price': 1.5},
'4': {'name': '果汁', 'price': 4.0}
}
total_price = 0.0
while True:
choice = input("请选择饮品编号 (输入 q 结束选择): ")
if choice == 'q':
break
if choice in goods:
quantity = int(input("请输入购买数量: "))
price = goods[choice]['price']
total_price += price * quantity
else:
print("无效的饮品编号,请重新选择。")
print(f"总额为: ¥{total_price}")
def main():
show_goods()
total()
main()
1.7 函数的特殊形式
除了按标准定义的函数外,Python还提供了两种具有特殊形式的函数。
(1)递归函数
若在函数内部调用了自身,则这个函数被称为递归函数。
递归函数通常用于解决结构相似的问题,它采用递归的方式,将一个复杂的大型问题转化为与原问题结构相似
的、规模较小的若干子问题,之后对最小化的子问题求解,从而得到原问题的解。
# 递归函数在定义时需要满足2个基本条件:
# (1)递归公式:求解原问题或相似的子问题的结构
# (2)边界条件:最小化的子问题,也是递归终止的条件
# 递归函数的执行分为2个阶段:
# (1)递归:递归本次的执行都基于上一次的运算结果
# (2)回溯:遇到终止条件时,则沿着递归往回一级一级地把值返回来
# 递归函数的定义格式:
def 函数名([参数列表]):
if 边界条件:
return 结果
else:
return 递归公式
案例:编写代码实现 n!求解
def function(num):
if num == 1: # 边界条件
return 1
else: # 递归公式
return num * function(num - 1)
num = int(input('请输入一个整数:'))
result = function(num)
print(f'{num}!=%d'%result)
案例:利用递归实现根据月份计算兔子总数量的功能
# 斐波那契数列(Fibonacci sequence),又称黄金分割数列,又称为“兔子数列”。
# 它的规律是:这个数列从第 3 项开始,每一项都等于前两项之和。
# 数学上,斐波那契数列以如下被以递推的方法定义:
# F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
def fibonaci(month):
if month == 0 or month == 1:
return 1
else:
return fibonaci(month-1) + fibonaci(month - 2)
# 测试经过12个月份后的兔子对数
result = fibonaci(12)
print(result)
案例:利用递归实现归并排序算法
# 归并排序是建立在归并操作上的一种有效的排序算法,该算法将已有序的子序列合并,得到完全有序的序列
# 即先使每个子序列有序,再使子序列段间有序。
#归并排序算法有两个基本的操作:
# 一个是分,也就是把原数组划分成两个子数组的过程。
# 另个是治,它将两个有序数组合并成一个更大的有序数组。
#将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素
#这时,可以认为只包含一个元素的子表是有序表。
#将子表两两合并,每合并一次,就会产生一个新的且更长的有序表
#重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表
# [6,5,7,4,3,1]
def merge_sort(li):
n = len(li)
if n == 1:
return li
# 把数据分成左右两部分
mid = n//2
left = li[:mid]
right = li[mid:]
# 递归拆分
left_res = merge_sort(left)
right_res = merge_sort(right)
# 把下层返回上来的数据,组成有序序列
result = merge(left_res,right_res)
# 合并
return result
def merge(left,right):
result = []
left_index = 0
right_index = 0
while left_index < len(left) and right_index < len(right):
if left[left_index] <= right[right_index]:
result.append(left[left_index])
left_index += 1
else:
result.append(right[right_index])
right_index += 1
# while循环结束后,把剩下的数据添加进来
result += right[right_index:]
result += left[left_index:]
return result
if __name__ == '__main__':
l = [6,5,7,4,3,1]
print(merge_sort(l))
(2)匿名函数
匿名函数是一类无需定义标识符的函数 ,它与普通函数一样可以在程序的任何位置使用
# 匿名函数的语法格式:
lambda <形式参数列表>:<表达式>
# 普通函数在定义时有名称,而匿名函数没有名称
# 普通函数的函数体中包含多条语句,而匿名函数的函数体只能是一个表达式
# 普通函数可以实现比较复杂的功能,而匿名函数可实现的功能较简单
# 普通函数能被其他程序使用,而匿名函数不能被其他程序使用
案例:定义一个计算数值平方的匿名函数
# 定义好的匿名函数不能直接使用,最好用一个变量保存它,以便后期可以随时使用这个函数。
temp = lambda x:pow(x,2) # 变量temp作为匿名函数的临时名称来调用函数
print(temp(10))
案例:学生管理系统
"""
使用自定义函数,完成对程序的模块化
学生信息包含:姓名、性别、手机号
该系统具有的功能:添加、删除、修改、显示、退出系统
设计思路:
提示用户选择功操作
获取用户选择的功能
根据用户的选择,分别调用不同的函数
"""
# 新建一个列表,用来保存学生的所有信息
stu_info = []
# 功能打印
def print_menu():
print('=' * 30)
print('学生管理系统')
print('1.添加学生信息')
print('2.删除学生信息')
print('3.修改学生信息')
print('4.显示所有学生信息')
print('0.退出系统')
# 定义一个添加学生信息的函数
def add_stu_info():
# 提示并获取学生的姓名
new_name = input('请输入新学生的姓名:')
# 提示并获取学生的性别
new_sex = input('请输入新学生的性别:')
# 提示并获取学生的手机号
new_phone = input('请输入新学生的手机号码:')
new_info = dict()
new_info['name'] = new_name
new_info['sex'] = new_sex
new_info['phone'] = new_phone
stu_info.append(new_info)
# 定义删除学生信息的函数
def del_stu_info(student):
del_num = int(input('请输入要删除的序号:')) - 1
del student[del_num]
# 定义修改学生信息的函数,具体代码如下:
def modify_stu_info():
if len(stu_info) !=0:
stu_id = int(input('请输入要修改学生的序号:'))
new_name = input('请输入要修改学生的姓名:')
new_sex = input('请输入要修改学生的性别:(男/女)')
new_phone = input('请输入要修改学生的手机号码:')
stu_info[stu_id - 1]['name'] = new_name
stu_info[stu_id - 1]['sex'] = new_sex
stu_info[stu_id - 1]['phone'] = new_phone
else:
print('学生信息表为空')
# 定义显示学生信息的函数
def show_stu_info():
print('学生的信息如下:')
print('=' * 30)
print('序号 姓名 性别 手机号码')
i = 1
for tempInfo in stu_info:
print("%d %s %s %s" %
(i, tempInfo['name'], tempInfo['sex'], tempInfo['phone']))
i += 1
# 在main函数中执行不同的功能
def main():
while True:
print_menu() # 打印菜单
key = input("请输入功能对应的数字:") # 获取用户输入的序号
if key == '1': # 添加学生信息
add_stu_info()
elif key == '2': # 删除学生信息
del_stu_info(stu_info)
elif key == '3': # 修改学生信息
modify_stu_info()
elif key == '4': # 查看所有学生的信息
show_stu_info()
elif key == '0':
quit_confirm = input('亲,真的要退出么?(Yes or No):')
if quit_confirm == 'Yes':
break # 跳出循环
else:
print('输入有误,请重新输入')
if __name__ == '__main__':
main()
案例:利用函数输出 1~100 中的偶数之和
def event_num_sum():
result = 0
counter = 1
while counter <= 100:
counter += 1
if counter % 2 == 1:
continue
result += counter
return result
print(event_num_sum())
案例:利用函数计算 20×19×18×…×3的结果
def func(num):
if num == 2:
return 1
else:
return num * func(num - 1)
result = func(20)
print(result)
案例:利用函数判断用户输入的整数是否为回文数
# 回文数:是一个正向和逆向都相同的整数,如12344321
def is_palindrome():
num = input('请输入整数:\n')
palindrome_num = num[::-1]
return num == palindrome_num
print(is_palindrome())
案例:利用函数判断用户输入的3个数字是否能构成三角形的三条边
def triangle():
side_length_one = int(input("请输入第一个边长:\n"))
side_length_two = int(input("请输入第二个边长:\n"))
side_length_three = int(input("请输入第三个边长:\n"))
if (side_length_one + side_length_two > side_length_three and
side_length_one + side_length_three > side_length_two and
side_length_two + side_length_three > side_length_one):
return "能构成三角形"
else:
return "不能构成三角形"
print(triangle())
案例:求 2 个正整数的最小公倍数
def lcm(x, y):
# 获取最大的数
if x > y:
greater = x
else:
greater = y
while True:
if greater % x == 0 and greater % y == 0:
lcm = greater
break
greater += 1
return lcm
# 获取用户输入
num1 = int(input("输入第一个数字: "))
num2 = int(input("输入第二个数字: "))
print(num1, "和", num2, "的最小公倍数为", lcm(num1, num2))