函数的定义
函数是组织好的、可重复使用的,用来实现特定功能的代码片段。
定义与调用语法:
# 定义函数
def 函数名(参数列表):
函数体
......
return 返回值
# 调用函数
函数名(参数)定义与调用样例:
# 定义函数
def say_hello():
print("大家好!")
# 调用函数
say_hello()**提示:**函数定义时的参数列表与返回值语句是可有可无的(由需求确定),函数必须先定义后调用执行(函数定义时,内部代码并未执行,在调用时才执行)。
注意:
- 函数必须先定义,在调用
- 函数定义时,并不会执行,只有在调用函数时,函数体的逻辑才会运行
- 函数中通过缩进来描述归属关系
函数的变量作用域
- 变量的作用域指的是变量的作用范围,也就是说这个变量,在哪里可以使用,在哪儿不可以使用。
- 根据作用范围的不同,常见的变量作用域分为两类:全局变量 和 局部变量。
"""
全局变量:在函数、推导式之外定义的变量,称之为全局变量,在整个文件中(包括函数内)都可以使用(通常定义在文件的顶部)。
局部变量:在函数、推导式内部定义的变量,称之为局部变量,只能在该函数内部使用,外部无法访问(执行完毕后,会自动销毁其内部局部变量) 。
优先级:在使用变量时,优先在当前作用域范围内查找,如果未查询到,再到上一层作用域查询。
"""
tax_rate = 0.05 #税率
# 定义函数
def cal_tax(total_price):
tax_rate = 0.08
if total_price >= 10000: #金额大于等于10000
tax = total_price * tax_rate #按税率计算税费
elif total_price >= 5000: #金额大于等于5000
tax = total_price * tax_rate * 0.8 #按税率打8折计算税费
else:
tax = 0 #金额小于5000,不缴税
return tax #返回税费
# 调用函数
my_tax = cal_tax(10000)
print(f"税前价格: 10000, 税: {my_tax}")
"""
当我们在执行Python代码时,对应的优先级顺序为:优先在当前作用域范围内查找,如果未查找到,再到上一层作用域查询 。
也就意味着,当我们在调用 cal_tax(1000) 这个函数的时候,用到 tax_rate 这个变量,那这个变量优先使用的是在函数内部所定义的这个局部变量 tax_rate = 0.08,而并非全局变量; 而如果在函数内部并未定义这个局部变量,此时才会使用全局变量 tax_rate 。
"""global关键字
那如果我们需要在函数内部,需要使用全局变量 并 修改全局变量的值(对全局变量重新赋值),此时,就可以使用python中提供的关键字 global。
global关键字用于明确的告诉Python解释器,在函数中要使用全局变量,使得可以在函数内部修改全局变量的值。
# 全局变量 : 在函数外部 或 函数的内部都是可以访问的 ;
tax_rate = 0.05
# 定义函数
def cal_tax(total_price):
global tax_rate # 告诉Python解释器, 函数中使用的是全局变量 tax_rate
tax_rate = 0.08 # 修改全局变量的值
if total_price > 10000:
tax = total_price * tax_rate
elif total_price >= 5000:
tax = total_price * tax_rate * 0.8
else:
tax = 0
return tax
my_tax = cal_tax(10000)
print(f"税前价格: 10000, 税: {my_tax}")
print(tax_rate)注意:
在基于global声明全局变量时,要先声明,再使用。
尽量避免在函数中使用全局变量,因为会使代码难以维护和调试
考虑使用函数参数和返回值来传递数据,而不是依赖全局变量
global主要用在程序的状态、配置和计数器等场景中
参数与返回值
在定义函数时,根据业务需要,可以指定参数与返回值,具体格式如下:
# 定义函数
def 函数名(形参列表):
函数体
......
return 返回值
# 调用函数
函数名(实参)- 形参(形式参数):函数定义时括号里的参数,只能在函数内使用(局部变量)
- 实参(实际参数):函数在实际调用时传入的参数。
案例如下:
# 函数1: 计算圆的面积(circle_area)并返回 -- 半径
# 定义函数
def circle_area(r):
area = 3.14 * r ** 2
return area # 返回结果
# 调用函数
result = circle_area(10) # result表示结果的意思
print(f"圆的面积为:{result}")
# 函数2: 计算长方形的面积(rectangle_area)并返回 -- 长, 宽
# 定义函数
def rectangle_area(l, w):
return l * w
# 调用函数
result = rectangle_area(10, 5)
print(f"长方形的面积为:{result}")
# 函数3: 计算长方形的周长和面积(rectangle_perimeter_area)并返回 -- 长, 宽
# 定义函数
def rectangle_perimeter_area(l, w):
return (l + w) * 2, l * w
# 调用函数
result = rectangle_perimeter_area(10, 5)
print(result, type(result)) # (30, 50) <class 'tuple'>
print(f"长方形周长:{result[0]},长方形面积:{result[1]}")
# 解包
perimeter,area = rectangle_perimeter_area(10, 5)
print(f"长方形周长:{perimeter},长方形面积:{area}")注意:
- 函数定义时如果有多个参数,多个参数之间使用逗号(,)分隔 。
return语句只有返回功能,而没有输出打印的功能,如果要输出,需要结合print()函数来实现 。- 函数是可以定义多个返回值的,多个返回值会封装到一个元组中。
位置参数
位置参数:调用函数时根据函数定义时的位置来传递参数。
# 定义函数
def reg_stu( name, age,gender, city):
print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
return {"name": name, "age": age, "gender": gender, "city": city}
# 位置参数:调用函数时根据函数定义时的位置来传递参数。
stu1 = reg_stu("张益达",20,"男","武汉")
print(stu1)关键字参数
**关键字参数:**调用函数时以形参名称作为关键字,以 "形参名=值" 的形式来传递参数(不要求顺序)。
**特别注意:**如果位置参数与关键字参数混用,关键字参数必须在位置参数之后(关键字参数之间,没有顺序要求)
# 关键字参数:调用函数时以形参名称作为关键字,以 "形参名=值" 的形式来传递参数(不要求顺序)。
# 定义函数
def reg_stu(name, age, gender, city):
print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
return {"name": name, "age": age, "gender": gender, "city": city}
# 关键字参数:调用函数时以形参名称作为关键字,以 "形参名=值" 的形式来传递参数(不要求顺序)。
# stu2 = reg_stu(name="张伟",age=20,gender="男",city="武汉")
stu2 = reg_stu(age=20,name="张伟",city="武汉",gender="男")
print(stu2)
# 位置参数和关键字参数混用:要求:如果位置参数与关键字参数混用,关键字参数必须在位置参数之后(关键字参数之间,没有顺序要求)
stu3 = reg_stu("张大炮",23,city="武汉",gender="男")
# stu3 = reg_stu(name="张大炮",age=23,"男","武汉") # 报错:位置参数必须写在关键字参数前面
print(stu3)那针对于这两种传参方式,都可以完成参数传递,那我们在项目开发中,到底应该使用哪种方式呢? 接下来,我们就通过一张表格对比一下:
| 位置参数 | 关键字参数 | |
|---|---|---|
| 形式 | s = calc(87, 68, 92, 85) | s = calc(math=87, chinese=68, english=92, computer=85) |
| 优点 | 简洁 | 可读性强、易维护和扩展 |
| 缺点 | 可读性差、易出错、维护难 | 代码繁琐 |
| 场景 | 参数少(一般不超过3个),且类型不同 | 参数较多,类型相同或容易混淆的场景 |
扩展:参数分隔符
特别注意:请勿滥用!!!
1. / 仅限位置参数示例
/左侧参数只能位置传参,不能关键字传参
# a,b在/的左边:只能按位置传入;
# c,d在/的右边:既可以位置,也可以关键字传参。
def func(a, b, /, c, d):
print(a, b, c, d)
# ✅正确:a,b使用位置传参
func(10, 20, 30, 40)
func(10, 20, c=30, d=40)
# ❌错误!a,b在 / 左边,不允许关键字传参
# func(a=10, b=20, c=30, d=40)2.* 仅限关键字参数分隔符(单独的,不是*args)*
* 右侧参数只能关键字传参,不能位置传参
def func(a, b, *, c, d):
print(a, b, c, d)
# ✅正确:c、d必须关键字传参
func(1, 2, c=3, d=4)
# ❌错误!*后面的c,d不能用位置传参
# func(1,2,3,4)3./ 和 * 写在同一个函数里(完整组合)
def demo(a, b, /, mid, *, x, y):
"""
a,b:只能位置传参
mid:可以位置 / 关键字传参
x,y:只能关键字传参
"""
print(a, b, mid, x, y)
# ✅合法调用
demo(10, 20, 30, x=100, y=200)
demo(10, 20, mid=30, x=100, y=200)
# ❌错误:a,b使用关键字
# demo(a=10,b=20, mid=30, x=100,y=200)
# ❌错误:x,y使用位置传参
# demo(10,20,30,100,200)4.区分容易混淆的写法
# 1./ 分隔符:限定左侧只能位置
def f(a, b, /, c): pass
# 2. * 分隔符:限定右侧只能关键字
def f(a, b, *, c): pass
# 3. *args:收集可变位置参数(收集多参数元组)
def f(*args): pass
# 4.**kwargs:收集可变关键字参数(收集字典)
def f(**kwargs): pass默认参数
默认参数也称为缺省参数,用于在定义函数时,为参数提供默认值,调用函数时,可以不传递有默认值的参数。
# 定义函数
def reg_stu(name, age, gender, city='北京'):
print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
return {"name": name, "age": age, "gender": gender, "city": city}
# 调用函数
stu = reg_stu("张三", 18, "男")
print(stu)
stu = reg_stu("赵四", 22, "男", "深圳")
print(stu)注意:
- 函数调用时,如果为默认参数传递了值,则会使用传递进来的值;如果没有传递该参数,则直接使用默认值。
- 默认参数必须放在没有默认值的参数列表的后面,一个函数在定义时是可以设置多个默认参数的。
"""
默认参数:也称为缺省参数,用于在定义函数时,为参数提供默认值,调用函数时,可以不传递有默认值的参数。
注意1:函数调用时,如果为默认参数传递了值,则会使用传递进来的值;如果没有传递该参数,则直接使用默认值。
注意2:默认参数必须放在没有默认值的参数列表的后面,一个函数在定义时是可以设置多个默认参数的。
"""
# 定义函数
def reg_stu( name, age,gender, city="武汉"): # city="武汉"就是默认值
print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
return {"name": name, "age": age, "gender": gender, "city": city}
# 调用函数
stu1 = reg_stu("张益达",22,"男") # 没有给city赋值,就使用默认值"武汉"
print(stu1) # {'name': '张益达', 'age': 22, 'gender': '男', 'city': '武汉'}
# 注意1:函数调用时,如果为默认参数传递了值,则会使用传递进来的值;如果没有传递该参数,则直接使用默认值。
stu2 = reg_stu("张伟",22,"男","北京")
print(stu2)
# 注意2:默认参数必须放在没有默认值的参数列表的后面,一个函数在定义时是可以设置多个默认参数的。
def reg_stu2( name, age,gender="男", city="武汉"): #允许: gender="男", city="武汉"就是默认值
print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
return {"name": name, "age": age, "gender": gender, "city": city}
# def reg_stu3( name, age=0, gender, city="武汉"): #报错:默认参数必须放在没有默认值的参数列表的后面
# print(f"注册成功,姓名:{name}, 年龄:{age}, 性别:{gender}, 城市:{city}")
# return {"name": name, "age": age, "gender": gender, "city": city}不定长参数
不定长参数也叫可变参数,用于定义及调用函数时参数个数不确定(0个或多个)的场景。对于不定长参数,根据传参方式的不同,又分为两类:位置参数 与 关键字参数。
不定长度位置参数
不定长位置参数指的是,通过位置参数形式传递进来的,长度不确定的参数。具体参数列表的书写形式如下
# 不定长位置参数指的是,通过位置参数形式传递进来的,长度不确定的参数。具体参数列表的书写形式如下:
# 定义函数,返回给定的一批数据的最大值、最小值、平均值(保留2位小数)
def calc_data(*args): # args是一个元组,python解释器会将多个位置参数封装到args元组中
print(args,type(args)) # (388, 39, 105) <class 'tuple'>
max_value = max(args)
min_value = min(args)
avg_value = round(sum(args) / len(args), 2)
return max_value, min_value, avg_value
# 调用函数,传递的参数个数不固定
min_v, max_v, avg_v = calc_data(388, 39, 105)
# min_v, max_v, avg_v = calc_data(15, 18, 3, 27, 89, 35)
# min_v, max_v, avg_v = calc_data(100, 45, 78, 15, 183, 96, 27, 89, 35)
print("最小值:", min_v, "最大值:", max_v, "平均值:", avg_v)不定长位置参数的写法 *args ,传递的所有匹配的位置参数都会被 args 变量收集,这些参数会合并封装为一个元组tuple中,args是元组类型(注意并不会封装关键字参数)。
注意:args 只是约定俗成的变量名,并不是关键字,这里可以使用任何合法的变量名(如 *data)。
不定长度关键字参数
- 不定长关键字参数指的是,通过关键字参数形式传递进来的,长度不确定的参数。具体参数列表的书写形式如下:
# 需求:定义函数,返回给定的一批数据的最大值、最小值、平均值(保留2位小数)
def calc_data(*args,**kwargs):
"""
根据传入的这批数据, 计算这批数据的最小值, 最大值, 平均值
:param args: 不定长位置参数, 需要计算的这批数据
:param kwargs: 不定长关键字参数
rd: 保留的小数位个数
print: 是否打印输出,True表示打印,False表示不答应
:return: 最小值, 最大值, 平均值
"""
# 计算最大值、最小值、平均值并返回
max_value = max(args)
min_value = min(args)
avg_value = sum(args) / len(args)
# 判断是否有round选项,有就保留指定位置
if "rd" in kwargs:
avg_value = round(avg_value, kwargs.get("rd"))
# 判断是否有print选项,如果有就打印结果
if "print" in kwargs and kwargs['print']:
print(f"计算出来的最小值: {max_value}, 最大值: {min_value}, 平均值:{avg_value}")
return max_value, min_value, avg_value
# 调用函数
result = calc_data(1, 2, 3, 4, 5, 3, 6, 3,rd=3,print=False)
print(result)- 不定长关键字参数的写法是
**kwargs,传递的参数是以"键=值"形式传递的关键字参数,这些"键=值"参数都会被kwargs接收,并合并封装为一个字典类型 。 - 注意:
kwargs只是约定俗成的变量名,并不是关键字,这里可以使用任何合法的变量名(如**options)。
不定长参数的总结
- 什么是不定长参数?
- 参数个数不确定,此时就可以使用不定长参数解决这类问题
- 不定长参数的分类?
*args:不定长位置参数,函数调用时,通过位置参数传递多个参数封装到一个元组(tuple)中**kwargs:不定长关键字参数,函数调用时,通过关键字参数传递多个参数封装到一个字典(dict)
*args与**kwargs的应用场景?
*args适用于处理数量不确定的数据**kwargs适用于处理数量不确定的选项
参数类型
在定义函数时,关于函数的参数类型可以是普通参数,也可以是特殊参数。普通参数,比较常见的就是:float、int、bool、str、list、set、tuple、dict等。我们之前定义的函数,参数基本都是普通类型的参数,比如:
def circle_area(r): # 参数是 int、float 类型
area = 3.14 * r ** 2
return area
area = circle_area(10)
print(area)
def calc_score(score_list): # 参数 是 list 列表类型
max_s = max(score_list)
min_s = min(score_list)
avg_s = round(sum(score_list) / len(score_list), 1)
return max_s, min_s, avg_s
s_list = [589, 609, 605, 643, 677, 455, 477, 489, 503]
max_score, min_score, avg_score = calc_score(s_list)函数本身作为参数的传递
特殊参数,比如:函数。 也就是说,我们是可以把一个函数作为参数,在调用另一个函数时,将其传递进入。比如:
# 函数的参数类型
# 加
def add(x, y):
return x + y
# 减
def subtract(x, y):
return x - y
# 乘
def multiply(x, y):
return x * y
# 除
def divide(x, y):
return x / y
# 计算
def calc(x, y, oper):
return oper(x, y)
print(calc(10, 20, multiply))匿名函数
匿名函数指没有名称的函数,需要通过lambda表达式来声明函数,可以简化简单函数的编写(单行表达式)。
# 命名函数和匿名函数的语法对比如下:
# 定义命名函数
def 函数名(参数列表):
函数体...
# 定义匿名函数
lambda 参数列表 : 函数体
say_hello = lambda : print('我爱中国')
add = lambda x, y: x + y
say_hello()
print(add(100, 200))**注意:**函数逻辑比较简单(单行表达式)且只在一个地方使用时,例如:作为其它函数的参数传递。
注意:匿名函数中必须是一个表达式,会自动将表达式的运行结果作为返回值。如果执行的是 print() 这类无返回值的操作,lambda 会返回 None。
# 需求: 完成如下列表的排序操作, 按照每一个元素的字符个数, 从小到大排序 ;
data_list = ["C++", "C", "python", "jack", "PHP", "Java", "Go", "JavaScript", "Rust"]
print(f"排序前:{data_list}")
# 默认排序:A->B->C->D->...->Z->a->b->c->d->...->z
# data_list.sort()
# print(f"排序后:{data_list}")
# 自定义排序规则:对列表中的字符串按照字符个数升序排序
# def sort_rule(s): # 定义排序规则,根据字符个数排序就返回每个字符串的长度
# return len(s)
# data_list.sort(key=sort_rule)
#---使用匿名函数优化---
# data_list.sort(key=lambda s: len(s)) # 按照字符个数升序
data_list.sort(key=lambda s: len(s),reverse=True) # 按照字符个数降序
print(f"排序后:{data_list}")案例中,调用的list列表的sort方法,是进行排序操作的,会根据key传入进来的函数进行排序,具体根据什么排序,是依据函数的返回值进行从小到大排序的。
命名函数与匿名函数怎么选择:
- 建议使用匿名函数的情况:函数逻辑简单,只在一个地方调用,例如:作为其它函数的参数传递
- 建议使用命名函数的情况:函数逻辑复杂,需要多步操作,需要多个地方重复使用或需要加文档说明的场景
函数的递归
递归调用:指的是在函数中自己调用自己的情况,先层层递进, 再逐层回归 ----> 一定得有终结点(避免死循环)
"""
# 案例: 计算n的阶乘
# 递归调用(先层层递进, 再逐层回归): 指的是在函数中自己调用自己的情况 ----> 一定得有终结点
jc(10) = 10 * jc(9)
jc(9) = 9 * jc(8)
jc(8) = 8 * jc(7)
jc(7) = 7 * jc(6) = 7 * 720 = 5040
jc(6) = 6 * jc(5) = 6 * 120 = 720
jc(5) = 5 * jc(4) = 5 * 24 = 120
jc(4) = 4 * jc(3) = 4 * 6 = 24
jc(3) = 3 * jc(2) = 3 * 2 = 6
jc(2) = 2 * jc(1) = 2 * 1 = 2
jc(1) = 1
"""
def jc(n):
if n == 1:
return 1
else:
return n * jc(n - 1)
result = jc(10)
print(result)函数的说明文档
- 函数的说明文档(Docstring)是写在函数开头,用三个引号包裹的字符串,用于解释函数的功能、参数、返回值等信息,方便调用者清楚函数的具体作用及细节。
# 函数2: 计算长方形的面积 -- 长, 宽
def rectangle_area(l,w):
"""
根据长方形的长度和宽度, 计算长方形的面积
:param l: 长度
:param w: 宽度
:return: 长方形的面积
"""
area = l * w
return area
print(rectangle_area(20, 10)):param:描述函数的参数:return:描述函数的返回值**查看函数说明文档:**在pycharm中,鼠标悬浮在函数上,自动展示。
嵌套调用
- 嵌套调用指的是在一个函数中,又调用了另外一个函数。
- 函数调用遵循栈结构,最后被调用的函数最先返回LIFO(Last In First Out,后进先出)
- 样例:
# 函数的嵌套调用
def function_a():
print("a ... before")
function_b()
print("a ... after")
def function_b():
print("b ... before")
function_c()
print("b ... after")
def function_c():
print("c ...")
function_a()
print("函数调用完毕 ~")- 执行流程:
- 调用
function_a()函数,输出 "a ... before" - 调用
function_b()函数,输出 "b ... before" - 调用
function_c()函数,输出 "c ..." function_c()函数执行完毕后,再执行function_b()函数的后半部分,输出 "b ... after"function_b()函数执行完毕后,再执行function_a()函数的后半部分,输出 "a ... after"function_a()函数执行完毕
- 调用
函数的案例
# 1. 定义函数:计算传入的字符串中元音字母的个数(元音字母为 aeiouAEIOU)
# 定义函数
def count_vowels(s): # s="s8ojwe7e3sau"
"""
计算传入的字符串中元音字母的个数(元音字母为 aeiouAEIOU)
:param s: 要统计的字符串
:return: 元音字母个数
"""
# 1 定义计数器变量count
count = 0
# 2 遍历字符串s得到每一个字符
for ch in s: # ch ="s",ch="8,...
# 3 判断是否是元音字母
if ch in "aeiouAEIOU":
# 4 如果是,计数器加1
count += 1
# 5 返回计数器变量count的值
return count
# 调用函数
count = count_vowels("s8ojwe7e3sau")
print(f"'s8ojwe7e3sau'中元音字母个数:{count}")
# 2. 定义函数:计算传入的班级学员高考成绩列表中成绩的最高分、最低分、平均分(保留1位小数),并返回。
# 定义函数
def calculate_scores(scores): # scores=[...]
"""
计算传入的班级学员高考成绩列表中成绩的最高分、最低分、平均分(保留1位小数),并返回。
:param scores: 成绩列表
:return: 最高分、最低分、平均分
"""
avg = round(sum(scores) / len(scores), 1) # round(数据值,保留的小数位数)
return max(scores), min(scores), avg
# 调用函数
max_score, min_score, avg_score = calculate_scores([589, 609, 605, 643, 677, 455, 477, 489, 503])
print(f"最高分:{max_score}分,最低分:{min_score}分,平均分:{avg_score}分")
"""
案例:开发一个教务管理系统,在该系统中可以维护和管理学员的成绩信息,具体需求如下:
1. 添加学生信息:根据提示录入学生姓名、语文、数学、英语成绩,录入完成保存到系统中。
2. 修改学生信息:要求输入要修改的学生姓名,然后再提示输入语文、数学、英语成绩,输入完成后修改学员信息。
3. 删除学生信息:要求输入要删除的学生姓名,根据姓名删除学生信息。
4. 查询学生信息:要求输入要查询的学生姓名,根据姓名查询学生信息并输出。
5. 列出所有学生:遍历所有学生信息并输出。
6. 退出系统。
"""
# 定义菜单内容
menu = """
# # # # # # # # # # # # # # # # # # # # # # 【菜单】 # # # # # # # # # # # # # # # # # # # # # # #
# 1. 添加学生信息 2. 修改学生信息 3. 删除学生信息 4. 查询学生信息 5. 列出所有学生 6. 退出系统 #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""
print("欢迎使用教务管理系统 ~")
# 存储全部的学生的列表容器
student_dict = {
'张三': {'chinese': 98.0, 'math': 92, 'english': 100},
'李四': {'chinese': 76, 'math': 84, 'english': 71},
'王五': {'chinese': 81, 'math': 64, 'english': 91},
}
def add_student():
# 判断用户是否存在,如果已经存在了就提示不要重复添加
name = input('请输入学生姓名:')
if name in student_dict:
print(f'【{name}】已经存在了,请勿重复添加')
return
# 如果学生不存在,则继续下一步的成绩录入的操作
chinese = float(input('请输入语文成绩:'))
math = float(input('请输入数学成绩:'))
english = float(input('请输入英语成绩:'))
student_dict[name] = {'chinese': chinese, 'math': math, 'english': english}
print(f'【{name}】添加成功')
# 打印存量数据的分割线
print("-----------------------当前用户如下:-----------------------")
# 调用之前的全量查看学生的方法
list_all_students()
print("-----------------------当前用户如下:-----------------------")
def update_student():
# 判断用户是否存在,不存在不能修改
name = input('请输入学生姓名:')
if name not in student_dict:
print(f'【{name}】用户不存在,不能进行修改操作')
return
# 用户存在的话,继续下一步修改的操作
chinese = float(input('请输入语文成绩:'))
math = float(input('请输入数学成绩:'))
english = float(input('请输入英语成绩:'))
student_dict[name] = {'chinese': chinese, 'math': math, 'english': english}
print(f'【{name}】修改成功')
# 打印存量数据的分割线
print("-----------------------当前用户如下:-----------------------")
# 调用之前的全量查看学生的方法
list_all_students()
print("-----------------------当前用户如下:-----------------------")
def delete_student():
# 判断用户是否存在,不存在不能删除
name = input('请输入学生姓名:')
if name not in student_dict:
print(f'【{name}】用户不存在,不能进行删除操作')
return
# 用户存在的话,继续下一步删除的操作
# 删除的操作分为两种
# 1.使用del
# del student_dict[name]
# 2.使用pop
student_dict.pop(name)
print(f'【{name}】删除成功')
# 打印存量数据的分割线
print("-----------------------当前用户如下:-----------------------")
# 调用之前的全量查看学生的方法
list_all_students()
print("-----------------------当前用户如下:-----------------------")
def search_student():
# 判断用户是否存在,不存在不能查询
name = input('请输入学生姓名:')
if name not in student_dict:
print(f'【{name}】用户不存在,不能进行查询操作')
return
# 根据name拿到用户的score
score = student_dict[name]
print(f"{name}的语文成绩是:{score['chinese']},数据成绩是:{score['math']},英语成绩是:{score['english']}")
def list_all_students():
# 如果字典为空,提示用户还没有学生
if not student_dict:
print('还没有一个学生,请添加了之后再过来')
return
for name, score in student_dict.items():
print(f"{name}的语文成绩是:{score['chinese']},数学成绩是:{score['math']},英语成绩是:{score['english']}")
while True:
print(menu)
# 定义操作类型
menu_type = input('请选择操作类型:')
# 使用match-case进行匹配
match menu_type:
case '1':
add_student()
case '2':
update_student()
case '3':
delete_student()
case '4':
search_student()
case '5':
list_all_students()
case '6':
break
case _:
print('操作有误,请检查后重试')类型注解
介绍
类型注解是Python中的一种语法特性,用于明确标识变量、函数参数和返回值的数据类型,从而使代码更清晰、更安全、更易维护。
# 定义变量
a: int | float = 695 #a的类型为int或者float类型
# a = 5.2
score: float = 98.5
hobby: str = "Python"
flag: bool = True
pic: None = None
names: list[str] = ["A", "C", "E"]
names: list[str | int] = ["A", "C", "E", 100]
phones: set[str] = {"13309091111", "15209109121"}
options: dict[str, int] = {"count": 0, "total": 0}
goods: tuple[str,int] = ("手机", 5999) #元组有几个类型就写几个值(有几个值就指定几个类型)
goods: tuple[str, int, int] = ("手机", 5999, 1)
# 需求:元组中有10个值,但是都是字符串或者int类型
goods: tuple[str|int,...] = ("手机", 5999, 1,"hello")注意:Python是动态类型语言,添加的类型注解只是提示,并不是强制约束!!!
扩展:
goods:tuple[str,int,int] = ("手机", 5999, 1) #表示goods元组中只能存三个值,且类型是str,int,int
goods2:tuple[str|int,...] = ("手机", 5999, 1,100) # 表示goods元组中可以存任意多个值,可以是str或者int类型,其中...省略号表示长度不固定。
类型推断
类型推断是指Python解释器自动推断出变量、表达式或函数返回值的数据类型的能力,而无需开发者显式声明。
函数的类型注解
例子:
def calc_capital_letter(s:str)->int:
"""
计算大写字母的个数并返回
:param s: 字符串
:return: 大写字母个数
"""
count=0
for ch in s:
if ch.isupper(): #判断是大写字母
count+=1
# 2 返回结果
return count
count = calc_capital_letter("AbcSs8H")
print(f"大写字母个数:{count}")多个参数的类型指定方式也是一样,在每一个参数的名称后面加上 参数名: 类型
def calc_order_cost(*args:tuple[str,float,int], coupon:int|float=0, score:int=0, express:float=0.0)->float:
"""
根据传入的一批商品信息(商品名、价格、数量)、优惠(优惠券、积分抵扣)、运费信息计算订单的总金额
:param args: 商品信息(商品名、价格、数量) ----> 如: ("鼠标", 188, 2) ("键盘", 388, 1)
:param coupon: 优惠券
:param score: 积分(100积分抵扣1元)
:param express: 运费
:return: 订单的总金额
"""
# 订单的总金额 = 商品总金额 - 优惠券 - 积分抵扣 + 运费
# 1 计算商品总金额
total_price = [goods[1]*goods[2] for goods in args]
total_cost = sum(total_price)
# 2 扣减优惠券
total_cost-=coupon
# 3 扣减积分抵扣
total_cost-=score//100
# 4 加运费
total_cost+=express
return total_cost
# 测试
total = calc_order_cost(("鼠标", 88.5, 2),("键盘", 388, 1),("手机", 6999, 1))
print(total)
total = calc_order_cost(("鼠标", 88.5, 2),("键盘", 388, 1),("手机", 6999, 1),coupon=100)
print(total)
total = calc_order_cost(("鼠标", 88.5, 2),("键盘", 388, 1),("手机", 6999, 1),coupon=100,score=1550)
print(total)模块
Python模块(module):一个**.py**文件就是一个模块,模块是Python程序的基本组织单位。在模块中可以定义变量、函数、类,以及可执行的代码。
那这个模块是我们自己定义的,那其实在Python中,还有很多的内置的模块,比如:数学计算模块 math、随机数模块 random、操作系统模块 os、系统参数模块 sys、日期时间模块 datetime、正则模块 re、csv文件操作模块 csv等等。
模块的导入
如果我们要使用Python官方提供的,或者第三方提供的模块,要想使用,比如得先导入才可以。 导入模块的具体语法如下:
| 导入形式 | 代码样例 | 调用方式 |
|---|---|---|
| import 模块名 | import random, os | 模块名.功能名 |
| import 模块名 as 别名 | import random as rd | 别名.功能名 |
| from 模块名 import 功能名 | from random import randint,choice | 功能名 |
| from 模块名 import 功能名 as 别名 | from random import randint as rint | 别名 |
| from 模块名 import * | from random import * | 功能名 |
使用案例如下:
# 1. 导入模块 import 模块名,模块名, [ as ... ] ---> 调用方式: 模块名.功能名 / 别名.功能名
# import 模块名
import random
print(random.randint(1, 20))
# import 模块名 as 别名
import random as r
print(r.randint(10,20))
# print(random.randint(10,20)) # 报错,一旦取了别名,就必须使用别名
# 2. 导入模块中的功能 from 模块名 import 功能名,功能名, ----> 调用方式: 功能名 / 别名
# from 模块名 import 功能名,功能名,...
from random import randint, randrange
print(randint(10, 20))
# from 模块名 import 功能名 as 别名
from random import randint as rint, randrange as rrange
print(rint(10, 20))
# from 模块名 import *
from random import * # *表示导入的"所有"功能
print(randint(10, 20))自定义模块
当我们在开发一些复杂的项目时,为了让项目结构更清晰,更便于项目的管理维护 以及 代码的复用,通常会将一个项目拆分为若干个模块。每个模块负责不同的职责,比如像如下这样:
config.py主要定义一些项目的配置信息session.py主要定义项目会话操作在核心业务代码
ai_partner.py中要想使用,就需要将其导入进来**提示:**每一个python文件都可以作为一个模块,模块的名字就是文件的名字(建议使用python标识符定义,规范命名) 。
代码演示1:
在 my.py 文件中定义常量, 函数
python# 常量(不会发生变化的数据 ; 常量的名称为全部大写) NAME = "我是名字" ADDRESS="我是地址" # 函数 def say1(): print("say1--->我❤️中国") def say2(): print("say2--->我❤️湖北") def say3(): print("say3--->我❤️武汉") def say4(): print("say4--->我❤️黑马") # def show(): # print("展示一下!!!")在
main.py文件中引入进来测试python# 1. 导入模块 import 模块名,模块名, [ as ... ] ---> 调用方式: 模块名.功能名 / 别名.功能名 import my my.say1() my.say2() my.say3() my.say4() print(my.NAME) print(my.ADDRESS)
代码演示2:
在 my.py 文件中定义常量, 函数
python# 常量(不会发生变化的数据 ; 常量的名称为全部大写) NAME = "我是名字" ADDRESS="我是地址" # 函数 def say1(): print("say1--->我❤️中国") def say2(): print("say2--->我❤️湖北") def say3(): print("say3--->我❤️武汉") def say4(): print("say4--->我❤️黑马") # 作用:定义其它模块使用from my import *导入哪些功能 __all__=["NAME","ADDRESS","say1","say2"]__all__是一个模块级别的特殊变量,用于指定from 模块名 import *时会导入哪些功能(*通配了哪些功能)。- 在
main.py文件中引入进来测试
python# 导入自定义模块中的功能 from my import * # 使用模块中的功能 say1() say2() say3() # 报错:在my .py的__all__中没有指定say3 print(NAME) print(ADDRESS)代码演示3:
- 在 my.py 文件中定义常量, 函数 ,还定义了调用
say1()和say2()等测试代码
python# 常量(不会发生变化的数据 ; 常量的名称为全部大写) NAME = "我是名字" ADDRESS="我是地址" # 函数 def say1(): print("say1--->我❤️中国") def say2(): print("say2--->我❤️湖北") def say3(): print("say3--->我❤️武汉") def say4(): print("say4--->我❤️黑马") # 作用:定义其它模块使用from my import *导入哪些功能 __all__=["NAME","ADDRESS","say1","say2"] print("------------以下是测试当前模块中定义的方法---------------") say1() say2() say3() say4()- 在
main.py文件中引入进来测试
python# 导入自定义模块中的功能 from my_fun import *我们会发现,my.py中的say1()、say2()、say3()、say4()方法都执行了,因为引入my.py中的功能,会将整个文件加载一次。如果期望导入的时候不执行测试代码,直接运行改文件时执行,那么就需要使用
__name__变量。__name__是Python中非常重要的内置变量,可用于区分当前模块(.py文件)是被导入执行的还是直接执行的。- 当模块直接运行时:
__name__的值为 "main"- 因此,如果这段测试代码,我们只想在模型直接运行的时候执行,模块被导入时不执行,就可以将测试代码放在if代码块中。
if __name__ == "__main__"
- 当模块被导入时:
__name__等于模块的文件名(不含.py后缀)
python# 常量(不会发生变化的数据 ; 常量的名称为全部大写) NAME = "我是名字" ADDRESS="我是地址" # 函数 def say1(): print("say1--->我❤️中国") def say2(): print("say2--->我❤️湖北") def say3(): print("say3--->我❤️武汉") def say4(): print("say4--->我❤️黑马") # 作用:定义其它模块使用from my import *导入哪些功能 __all__=["NAME","ADDRESS","say1","say2"] # 如果直接运行当前模块,__name__="__main__",如果当前模块被其它模块引入,__name__="模块名",也就是说在其他模块引用当前模块的时候,下面的测试代码并不会执行 if __name__=="__main__": say1() say2() say3() say4()- 在
包
我们在实际项目开发中,开发一个项目,会涉及到大量的模块,包括数据处理的相关模块、web应用相关模块、通用工具模块等等,涉及到的python文件也会比较多。
为了便于项目的管理和维护,我们可以把这些python文件,放在不同的目录下,那这个目录我们也称为软件包 package。比如 数据处理的相关模块,放在一个目录中,web应用相关模块,放在一个目录中;通用工具模块,放在一个目录中。
- **包:**本质就是一个文件夹,该文件夹中可以包含若干python模块(.py文件),文件夹下还包含了一个
__init__.py。 - **作用:**模块文件较多时,用来管理多个模块。(包的本质也是一个模块)
在 init.py 文件中就可以描述这个包的基本信息,包括版本、作者、import * 时导入的是哪些模块等。 定义好了包之后,在别的模块中要想使用,还是需要先将其导入进来。
- 包的导入方式:
| 导入形式 | 代码样例 | 调用方式 |
|---|---|---|
| import 包名.模块名 | import utils.my | 包名.模块名.功能名 |
| from 包名 import 模块名 | from utils import my | 模块名.功能名 |
| from 包名 import * | from utils import * | 模块名.功能名 |
| from 包名.模块名 import 功能名 | from utils.my import say1 | 功能名 |
| from 包名.模块名 import * | from utils.my import * | 功能名 |
- 演示代码:
- 定义一个目录utils,并在这个目录下面存放三个Python文件,分别命名为me.py和my.py,以及__init__.py
其中me.py文件的内容如下:
# 常量(不会发生变化的数据 ; 常量的名称为全部大写)
CLASSNAME = "测试"
NUMBER = 63my.py 中定义了几个函数:
# 常量(不会发生变化的数据 ; 常量的名称为全部大写)
NAME = "我是名字"
ADDRESS="我是地址"
# 函数
def say1():
print("say1--->我❤️中国")
def say2():
print("say2--->我❤️湖北")
def say3():
print("say3--->我❤️武汉")
def say4():
print("say4--->我❤️黑马")
# 作用:定义其它模块使用from my import *导入哪些功能
__all__=["NAME","ADDRESS","say1","say2"]
print("------------以下是测试当前模块中定义的方法---------------")
say1()
say2()
say3()
say4()然后在 init.py 中描述一下这个包的基本信息
# 描述包信息
__version__ = "1.0.0"
__author__ = "chengang"
# 定义当执行 `from 包名 import *` 的时候,哪些模块会被导出
__all__ = ["my", "me"]- 引入包的模块进行测试
# import 包名·模块名 调用方法:包名·模块名·功能名
import utils.my
utils.my.my_say1()
utils.my.my_say2()
print("----------------")
# from 包名 import 模块名 调用方法:模块名·功能名
from utils import me
print(me.CLASSNAME)
print(me.NUMBER)
print("----------------")
# 这个相当于引进所有的模块,然后通过模块进行调用
# from 包名 import * 调用方法:模块名·功能名
from utils import *
print(me.CLASSNAME)
print(me.NUMBER)
print(my.NAME)
print(my.ADDRESS)
print("----------------")
# 这里相当于直接引入包中的模块指定的方法名称
# from 包名.模块名 import 功能名 调用方法:功能名
from utils.my import my_say1,my_say2
my_say1()
my_say2()
print("----------------")
# 相当于引入包中指定模块下的全部方法
# from 包名.模块名 import * 调用方法:功能名
from utils.my import *
my_say1()
my_say2()**注意:**在通过 from 包名 import * 导入全部模块的时候,必须要在 __init__.py 文件中添加 __all__=[],控制允许导入的模块列表 。
导入包中的模块 或 导入包中模块中的功能,是比较灵活的,写法方法有很多,这里大家可以根据实际的需求来决定。