一、函数基础
1.1、函数的用法和底层分析
函数是可重用的程序代码块。
函数的作用,不仅可以实现代码的复用,更能实现代码的一致性。一致性指的是,只要修改函数的代码,则所有调用该函数的地方都能得到体现。
在编写函数时,函数体中的代码写法和我们前面讲述的基本一致,只是对代码实现了封装,并增加了函数调用、传递参数、返回计算结果等内容。
1.2、函数简介
1.2.1函数的基本概念
一个程序由一个一个的任务组成;函数就是代表一个任务或者一个功能(function)。
函数是代码复用的通用机制
1.2.2、函数的分类
内置函数
我们前面使用的
str()
、list()
、len()
等这些都是内置函数,我们可以拿来直接使用。标准库函数
我们可以通过
import
语句导入库,然后使用其中定义的函数第三方库函数
Python社区也提供了很多高质量的库。下载安装这些库后,也是通过
import
语句导入,然后可以使用这些第三方库的函数用户自定义函数
用户自己定义的函数,显然也是开发中适应用户自身需求定义的函数。今天我们学习的就是如何自定义函数。
1.2.3、 函数的定义和调用
实现某个功能的一段完整的代码code. 使用函数名称进行封装.以便 通过函数名调用,实现一次编写,多次调用的目的.
python语言中:封装函数尽量做到: 高内聚 低耦合(只完成自己的 功能, 函数执行成功与失败,与别的内容无关)
自定义函数语法:def 函数名(形式参数列表):
code # 函数体
return value
python
def add(a,b,c):
# 执行三个数相加,并返回和
sum=a+b+c
print(f'{a} {b} {c}三个数相加的和为:{sum}')
return sum
add(10,20,30)
我们使用def来定义函数,然后就是一个空格和函数名称;
Python执行def时,会创建一个函数对象,并绑定到函数名变量上。
参数列表圆括号内是形式参数列表,有多个参数则使用逗号隔开
定义时的形式参数不需要声明类型,也不需要指定函数返回值类型
调用时的实际参数必须与形参列表一一对应
return返回值如果函数体中包含return语句,则结束函数执行并返回值;
如果函数体中不包含return语句,则返回None值。
调用函数之前,必须要先定义函数,即先调用def创建函数对象内置函数对象会自动创建
标准库和第三方库函数,通过import导入模块时,会执行模块中的def语句
二、形参和实参
形参和实参的要点:
- 圆括号内是形式参数列表,有多个参数则使用逗号隔开
- 定义时的形式参数不需要声明类型,也不需要指定函数返回值类型
- 调用时的实际参数必须与形参列表一一对应
python
def add(a, b): # a 和 b 是形参
return a + b
result = add(5, 3) # 5 和 3 是实参
print(result) # 输出结果是 8
2.1、形参
定义:形参是函数定义时声明的变量,它们用来接收调用函数时传入的值。形参在函数内部使用,作用域是函数内部。
特点:
形参在函数定义时定义,通常是在函数头部括号内。
形参可以被视为占位符,用于代表将来的实际输入数据。
2.2、实参
定义:实参是调用函数时传入的实际值或变量。实参可以是字面量、变量、表达式、以及其他函数的返回值等。
特点:
实参是在函数调用时提供的,实际传递给函数的值。
实参的类型和数量需要和形参匹配。
三、函数的参数传递
3.1、位置参数
位置参数是最基本的参数传递方式,函数调用时参数的传递顺序必须与函数定义时的参数顺序一致
python
# 定义一个函数
def function(name, age, sex): # 需要传递三个参数
print(f"姓名: {name}, 年龄:{age}, 性别: {sex}")
# 调用这个函数
function("懒洋洋", 5, "Man")#姓名: 懒洋洋, 年龄:5, 性别: Man
3.2、关键字参数
关键字参数允许在调用函数时使用参数名来传递参数,这样可以不必考虑参数的顺序
python
# 定义一个函数 info: 做一个自我介绍
def info(name, age, score):
print(f"my name is {name}, age: {age}, score: {score}")
# 使用关键字去调用info 函数
info(age=18, score=100, name="小白")#my name is 小白, age: 18, score: 100
info("小白",score=90, age=6) # my name is 小白, age: 6, score: 90
3.3、缺省参数
缺省参数允许在定义函数时为某些参数提供默认值。如果在调用函数时没有提供这些参数的值,则使用默认值
python
# 定义一个函数 info: 做一个自我介绍
def info(name="某某某", age=18, score=100):
print(f"my name is {name}, age: {age}, score: {score}")
# 调用info函数
info() # info函数在调用的时候没有给出参数,则name,age, score使用的是默认值
info("小白") # 小白给出的是位置参数name的内容, age,score使用的是默认值
info(age=5, score=20) # age, score是关键字参数, name是没有给出值得.
# name使用的是默认值参数
info("小黑", age=10) # name是位置参数,age是关键字参数,score:默认值
info(age=18,"小白",score=20) # error :一旦使用了关键字参数,后面的内容就能再是
# 位置参数了。
3.4、不定长参数
不定长参数允许在调用函数时传递任意数量的位置参数和关键字参数。这种参数在函数定义时使用
*args
和**kwargs
3.4.1、*args
不定长位置参数使用
*args
接收多个位置参数,参数以元组的形式传递
python
def info(name, age, *hobby): # hobby参数前面有一个*表示的是不定长参数。
print(f"name:{name}, age: {age}")
print(f"hobby : {hobby}")
# hobby这个参数是一个元组
print(f"{type(hobby)}")
# 调用函数info # 小白 与 5 是位置参数,会给到name,age
# "足球", "篮球", "保龄球" 属于不定长参数,打包成元组,给到 hobby这个变量
info("小白", 5, "足球", "篮球", "保龄球")
# name:小白, age: 5
# hobby : ('足球', '篮球', '保龄球')
# <class 'tuple'>
print("*"*20)
info("小黑",10, "唱", "跳", "舞", "吃", "喝")
# name:小黑, age: 10
# hobby : ('唱', '跳', '舞', '吃', '喝')
# <class 'tuple'>
3.4.2、 **kwargs
不定长关键字参数使用
**kwargs
接收多个关键字参数,参数以字典的形式传递
python
def info(name, age, **parent): # parent:不定长参数: 前面有**,会将键值对的
# 形式的参数打包成字典
print(f"name: {name}, age: {age}")
print(f"parent: {parent}")
print(f"parent: {type(parent)}")
# 调用info函数
info("小白", 5, mother="母亲", father="父亲")
# name: 小白, age: 5
# parent: {'mother': '母亲', 'father': '父亲'}
# parent: <class 'dict'>
3.4.3、组合
python
def info(name, age, *hobby, **parent):
print(f"name:{name}, age: {age}, hobby: {hobby}, parent:{parent}")
print(f'{type(hobby)}')
print(f'{type(parent)}')
# 调用info函数
info("小白", 5, "唱","跳", "篮球", mother="母亲", father="父亲")
'''
name:小白, age: 5, hobby: ('唱', '跳', '篮球'), parent:{'mother': '母亲', 'father': '父亲'}
<class 'tuple'>
<class 'dict'>
'''
# error,在* 与 **混用的时候,一旦使用了**传递键值对儿的时候,就不能再传递单个的参数了。
# info("小黑", 5, "唱", mother="母亲", father="父亲", "跳", "篮球")
四、函数的返回值
return
返回值要点:如果函数体中包含
return
语句,则结束函数执行并返回值如果函数体中不包含
return
语句,则返回None
值要返回多个值,使用列表、元组、字典、集合将多个值"存起来"即可
返回多个值,会将返回值的值打包成 一个元组。
定义一个函数,返回100 ~ 999之间的所有的水仙花数字。 水仙数数字有 4个:
pythondef flower(): # 定义一个列表,记录水仙花数字 ls = [] # 编写功能,找到这四个水仙花数字 for number in range(100, 1000): g = number % 10 s = (number // 10) % 10 b = number // 100 # 判断是不是水仙花数字 if number == g**3 + s**3 + b**3: ls.append(number) # 出了循环以后,ls里面的内容就是4个水仙花数字 a, b, c, d = ls[0], ls[1], ls[2], ls[3] return a, b, c, d # 接收函数的返回值 value1, value2, value3, value4 = flower() print(f"{value1}, {value2}, {value3}, {value4}")#153, 370, 371, 407 result = flower() print(f"result : {result}")#result : (153, 370, 371, 407)
五、全局变量和局部变量
5.1、全局变量
在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束。
全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。
global关键字作用: 声明使用的变量属于全局变量
global一般在函数的内部使用。
python
a = 100 #全局变量
def f1():
global a #如果要在函数内改变全局变量的值,增加global关键字声明
print(a) #打印全局变量a的值
a = 300
f1()#100
f1()#300
print(a)#300
5.2、局部变量
在函数体中(包含形式参数)声明的变量。
局部变量的引用比全局变量快,优先考虑使用
如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量
python
a=100
def f1():
a = 3 #同名的局部变量
print(a)
f1()#3
print(a) #a仍然是100,没有变化
5.3、局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。
在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运行速度。
python
import time
a = 1000
def test01():
start = time.time()
global a
for i in range(100000000):
a += 1
end = time.time()
print("耗时{0}".format((end-start)))
def test02():
c = 1000
start = time.time()
for i in range(100000000):
c += 1
end = time.time()
print("耗时{0}".format((end-start)))
test01()#耗时6.526300430297852
test02()#耗时5.019754648208618
print(globals())
5.4、nonlocal和global关键字
nonlocal 用来在内部函数中,声明外层的局部变量。
global 函数内声明全局变量,然后才使用全局变量
python
#测试nonlocal、global关键字的用法
a = 100
def outer():
b = 10
def inner():
nonlocal b #声明外部函数的局部变量
print("inner b:",b)#inner b: 10
b = 20
global a #声明全局变量
a = 1000
inner()
print("outer b:",b)#outer b: 20
outer()
print("a:",a)#a: 1000
六、lambda表达式和匿名函数
lambda
表达式可以用来声明匿名函数。lambda
函数是一种简单的、在同一行中定义函数的方法。lambda
函数实际生成了一个函数对象。
lambda
表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。
lambda
表达式的基本语法如下:lambda arg1,arg2,arg3... : <表达式>
arg1
arg2
arg3
为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。
七、嵌套函数
嵌套函数:在函数内部定义的函数!
python
def outer():
print('outer')
def inner():
print('inner')
inner()
outer()
inner()
就是定义在outer()
函数内部的函数。inner()
的定义和调用都在outer()
函数内部
应用场景:
封装 - 数据隐藏
外部无法访问"嵌套函数"。
贯彻 DRY(Don't Repeat Yourself) 原则
嵌套函数,可以让我们在函数内部避免重复代码。
闭包
八、递归函数
递归(recursion)是一种常见的算法思路,在很多算法中都会用到。比如:深度优先搜索(DFS:Depth First Search)等。
递归的基本思想就是"自己调用自己"
递归函数指的是:自己调用自己的函数,在函数体内部直接或间接的自己调用自己。每个递归函数必须包含两个部分:
终止条件
表示递归什么时候结束。一般用于返回值,不再调用自己。
递归步骤
把第n步的值和第n-1步相关联。
⚠️递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数据时,谨慎使用。
使用递归函数计算一个数字的阶乘:
pythonn=5 def jiec(n): if n==1: return 1 return n*jiec(n-1) print(jiec(5))#120
九、eval()函数
功能:将字符串
str
当成有效的表达式来求值并返回计算结果。语法:eval(expression, globals=None, locals=None)
参数:
source
:一个Python表达式或函数compile()
返回的代码对象
globals
:可选。必须是dictionary
locals
:可选。任意映射对象
python
result = eval("2 + 3")
print(result) # 输出: 5
x = 10
result = eval("x * 2")
print(result) # 输出: 20
result = eval("max(5, 10, 15) + min(1, 2, 3)")
print(result) # 输出: 16
x = 1
def my_func():
y = 2
# 使用eval执行局部变量
return eval("x + y", globals(), locals())
print(my_func()) # 输出: 3
⚠️⚠️⚠️
eval函数
会将字符串当做语句来执行,因此会被注入安全隐患。比如:字符串中含有删除文件的语句。那就麻烦大了。因此,使用时候,要慎重!!!
安全性 :使用
eval()
可能存在安全风险,因为它会执行字符串中的任何 Python 代码。如果字符串来自不可信的来源(如用户输入),这可能导致代码注入风险。因此,在处理不安全的输入时,尽量避免使用eval()
。性能 :
eval()
的执行效率相对较低,因为它会运行解析和执行,极有可能在性能敏感的场合造成瓶颈。替代方案 :对于简单的数学表达式,可以使用
ast.literal_eval()
,它安全地求值字面量表达式,如字典、列表、元组等,不会执行其他代码。
python
import ast
str_expr = "[1, 2, 3, 4]"
result = ast.literal_eval(str_expr) # 安全地转换字符串为列表
print(result) # 输出: [1, 2, 3, 4]
十、LEGB规则
Local
指的就是函数或者类的方法内部
Enclosed
指的是嵌套函数(一个函数包裹另一个函数,闭包)
Global
指的是模块中的全局变量
Built in
指的是Python为自己保留的特殊名称
如果某个name
映射在局部local
命名空间中没有找到,接下来就会在闭包作用域enclosed
进行搜索,如果闭包作用域也没有找到,Python就会到全局global
命名空间中进行查找,最后会在内建built-in
命名空间搜索 (如果一个名称在所有命名空间中都没有找到,就会产生一个NameError
)
python
#测试LEGB
s = "global"
def outer():
s = "outer"
def inner():
s = "inner"
print(s)
inner()
outer()#inner
十一、学生管理系统(基于函数)
python
# 完成学生管理系统
# 1.创建一个班级信息: 班级使用列表这个容器进行表示
class_info = []
# 2. 定义一个python菜单函数
def print_menu():
print("****************************")
print("** 1. 增加学生 2. 删除学生 **")
print("** 3. 修改学生 4. 查询学生 **")
print("** 5. 显示所有学生信息 **")
print("** 6. 退出学生管理系统 **")
print("****************************")
# 定义一个增加学生信息的函数
def add_student():
"""
student 使用字典数据类型进行描述;
{
"name": name,
"age" : age,
"score" : score
}
"""
# 声明使用的是全局变量
global class_info
# 从键盘中输入学生的信息
name = input("please input student name : >> ")
age = int(input("please input student age : >> "))
score = int(input("please input student score : >>"))
# 需要对学生的信息进行验证
# 对名字进行重名认证,如果重复名字了,就不让在添加信息
# class_info = [{"name":name, "age":age, "score":score}, {},{}]
# 使用for循环进行遍历
for info in class_info:
# 如果class_info里面有数据,则info是学生的个人信息
if info["name"] == name:
print("您输入的学生已经存在,添加学生失败")
return -1
# 对年龄判断
if 0 > age > 100:
print("您输入的年龄有误,添加学生失败")
return -2
# 对成绩的判断
if 0 > score > 100:
print("您输入的成绩有误,添加学生失败")
return -3
# 通过输入的信息组建成一个字典
student = {"name": name, "age": age, "score": score}
# 将学生信息加入到列表当中
class_info.append(student)
print(f"添加学生{name}信息成功")
return 0
# 删除学生
def remove_student():
""" 删除学生信息 """
# 1.global声明全局变量 class_info
global class_info
# 2.根据学生姓名删除学生信息
name = input("please input remove student name : >>")
# 3.根据学生的姓名找到需要删除的学生
for student in class_info:
if student["name"] == name:
# 通过姓名找到了需要删除的学生
""" python 语言中列表可以根据值删除 remove """
ret = input(f"您确定要删除{name}的信息吗?如果确认请输入yes or y")
if ret == "yes" or ret == "y":
class_info.remove(student)
print("删除成功")
return 0
else:
print("您的输入有误")
return -1
# for 循环结束, 代表了没有找到需要删除的学生
print("您输入的学生不存在")
return -2
# 修改学生信息函数
def modify_student():
""" 修改学生信息 """
# 声明使用全局变量
global class_info
# 通过学生的学号对学生的信息进行修改: 列表将其下标当做学号
number = int(input("please input student number >>"))
# 判断学号是否正确
if len(class_info) <= number < 0:
print("您输入的学号有误")
return -1
# 根据学号修改相应的信息
class_info[number]["name"] = input("请输入修改之后的学生姓名: ")
class_info[number]["age"] = int(input("请输入修改之后的学生年龄 :"))
class_info[number]["score"] = int(input("请输入修改之后的学生成绩 :"))
print("修改学生成功")
return 0
# 定义一个查找学生的函数
def find_student():
# 声明使用的变量是全局变量
global class_info
# 通过姓名查找学生的信息
name = input("please input student name : >>")
for info in class_info:
if info["name"] == name:
print(f"姓名: {info['name']}, age: {info['age']}, score:{info['score']}")
return 0
print("您输入的学生不存在")
return -1
# 显示所有的学生信息
def show_student():
# 声明使用的是全局变量
global class_info
for info in class_info:
print(f"姓名: {info['name']}, age: {info['age']}, score:{info['score']}")
def main():
""" 学生管理系统主程序 """
# 死循环管理学生系统
while True:
# 打印学生管理系统菜单
print_menu()
# 给出提示信息,让用户选择自己的操作
choose = int(input("please input your choose"))
match choose:
case 1:
add_student()
case 2:
remove_student()
case 3:
modify_student()
case 4:
find_student()
case 5:
show_student()
case 6:
print("bye~")
break
case _:
print("您的输入有误,请重新输入")
pass
# 调用main函数执行程序
main()