Python(二)
第六章 容器数据类型
1. 列表 List
1.1 列表的特点
- 有序列表:列表中的元素保持插入顺序
- 可变类型:创建后可以修改(增删改元素)
- 可包含任意类型:同一个列表可以包含数字、字符串、其他列表等不同类型
- 允许重复元素:同一个值可以出现多次
1.2 列表的创建
- 直接创建列表
- 通过 list() 函数创建列表
1.3 列表的基本操作
1. 访问列表
2. 列表运算
-
向列表中添加元素
-
修改列表中元素
-
删除列表中元素
-
列表相加
-
列表乘法
-
获取列表长度
-
列表的最大值和最小值
-
排序和反转
3. 遍历列表
- 使用for循环遍历集合
- 使用下标遍历集合
- 使用enumerate函数遍历集合
1.4 其它函数
| 函数 | 说明 |
|---|---|
| list.insert(index,x) | 在指定位置插入x |
| list.append(x) | 在列表末尾追加x |
| list1.extend(list2) | 在列表1的末尾追加列表2的数据 |
| del list[index] | 删除指定位置的数据或切片 |
| list.remove(x) | 删除第一次出现的x |
| list.pop([index]) | 删除指定位置的数据,默认为末尾数据 |
| list.clear() | 清空列表中元素 |
| list[index] = x | 修改指定位置的数据 |
| list1[start:end] = list2 | 修改列表切片的数据 |
| sorted(list[,reverse=True]) | 返回排序后的新列表,可选降序 |
| list.sort([reverse=True]) | 对列表就地排序,可选降序 |
| list.reverse() | 反转列表中的元素 |
| list.index(x[,start,[,end]]) | 返回x在列表中首次出现的位置,可指定起始和结束范围 |
| list.count(x) | 返回x的数量 |
| len(list) | 返回列表元素个数 |
| max(list) | 返回列表中最大值 |
| min(list) | 返回列表中最小值 |
| sum(list) | 返回列表中所有元素和 |
| list.copy() | 拷贝列表 |
| list(x) | 将序列转换为列表 |
2. 元组Tuple
2.1 元组的特点
-
使用语法:元组使用 () 定义,元素之间使用 , 分隔,单元素元组需要逗号
(只要元素之间有逗号,()可以省略)。
-
有序集合:元组中的元素保存插入顺序。
-
不可变类型:不能对元组中的不可变对象进行修改操作(增删该元素)
-
可包含任意类型:元组中元素可以是不同的类型。
-
允许重复元素:同一个值可以出现多次。
2.2 元组的创建
- 直接创建元组
- 通过 tuple() 函数创建元组
2.3 元组的基本操作
1. 访问元组
- 索引访问
- 切片访问
2. 元组运算
- 元组相加
- 元组相乘
- 元组的成员检测
- 元组的长度
- 元组的最大、最小值、求和
- 元组的比较
- 元组的排序
3. 遍历元组
- 直接通过遍历元组
- 通过下标遍历元组
- 将元组转换为列表
- 通过enumerate()同时获取元组的下标和元素
3.集合Set
3.1 集合的特点
- 使用语法:集合Set使用{}定义,元素之间使用,分割
- 无序集合:集合中的元素不以任何特定顺序存储,所以集合没有索引,也不能通过索引或切片方式访问集合元素
- 可变类型:可以对集合中的元素进行修改操作(增删该元素)
- 可以包含任意类型:集合中元素可以是不同类型
- 不允许重复元素:同一个值只能出现一次
- 支持集合运算:集合可以进行数学上的集合操作,如并集、交集和差集。
3.2 集合的创建
- 直接创建集合
- 通过 set() 函数创建集合
3.3 集合的基本操作
1. 访问集合
- 索引访问,没有。
- 切片访问,没有。
2. 集合运算
-
集合的交集
-
集合的并集
-
集合的差集
-
向集合中添加元素
-
从集合中删除元素
-
集合成员检测
-
获取集合长度
-
集合中元素的最大值、最小值、求和
-
集合的排序
-
集合的比较(== > < <= >=)
3. 遍历集合
- 使用for循环遍历集合
- 使用enumerate函数遍历集合
- 转换为列表后遍历集合
3.4 集合的常用函数
| 函数 | 说明 |
|---|---|
| set.add(x) | 添加元素 |
| set.update(x) | 添加元素,x可以为列表、元组、字符串、字典等可迭代对象 |
| set.union(x) | 添加元素后返回一个新的集合,x可以为列表、元组、字符串、字典等可迭代对象 |
| set.remove(x) | 从集合中移除x,x不存在则报错 |
| set.discard(x) | 从集合中移除x,x不存在也不报错 |
| set.pop() | 随机取出集合中的一个元素,如果集合为空则报错 |
| set.clear() | 清空集合 |
| set.difference(x1,…) | 求set1和x1的差集,返回一个新的集合 |
| set.difference_update(x1,…) | 求set1和x1的差集 |
| set.intersection(x1,…) | 求set1和x1的交集,返回一个新的集合 |
| set.intersection_update(x1,…) | 求set1和x1的交集 |
| set1 & set2 | 两集合求交集 |
| set1 | set2 | 两集合求并集 |
| set1 - set2 | 两集合求差集 |
| set1.isdisjoint(set2) | 判断两集合是否没有交集 |
| set1.issubset(set2) | 判断set1是否为set2的子集 |
| set1.issuperset(set2) | 判断set2是否为set1的子集 |
| set1.symmetric_difference(set2) | 求两集合中不重复的元素,返回一个新的集合 |
| set1.symmetric_difference_update(set2) | 求两集合中不重复的元素 |
| set.copy() | 拷贝集合 |
| len(set) | 返回集合元素个数 |
| max(set) | 求集合中元素的最大值 |
| min(set) | 求集合中元素的最小值 |
| sum(set) | 求集合中元素的加和 |
4.字典Dictionary
4.1 字典的特点
- 使用语法:字典使用 {} 定义,键(key)和值(value)使用 : 连接,每个键值对之间使用 , 分隔。如{key1 : value1, key2 : value2}
- 有序性:python3.7+版本,保证字典会记住键值对的插入顺序,但是字典没有索引,不能使用索引或切片访问,可以通过键访问对应的值。
- 可变类型:可以对字典中的元素(键值对)进行修改操作(增删改元素)
- 键值类型:值可以取任何数据类型,但键必须是不可变的,如字符串、数字、元组等。不能使用列表、集合或字典作为可变类型做为键。
- 键值重复性:字典中的键必须是唯一的,如果重复赋值,后面的值会覆盖前面的,value可以重复。
4.2 字典的创建
- 直接创建字典
- 通过 dict() 函数创建字典
4.3 字典的基本操作
1. 访问字典
- 使用key访问
- 使用get()访问
2. 字典运算
-
向字典中添加元素
-
修改字典中元素
-
删除字典中元素
-
字典key成员检测
-
获取字典长度
-
字典中最大、最小值查找
-
字典的排序
-
字典的默认值处理
-
字典的合并
3. 遍历字典
-
使用for循环遍历字典(key、value、key,value)
-
使用enumerate函数遍历字典
-
转换为列表后遍历字典
4.4 字典的常用函数
| 函数 | 说明 |
|---|---|
| del dict[key] | 根据key删除键值对 |
| dict.pop(key[,default]) | 获取key所对应的value,同时删除该键值对,可设置默认值 |
| dict.popitem() | 取出字典中的最后插入的键值对,字典为空则报错 |
| dict.clear() | 清空字典 |
| dict1.update(dict2) | 将dict2中的键值对更新到dict1中 |
| dict.get(key[,default]) | 获取字典中key对应value,可设置默认值 |
| dict.setdefault(key[,default]) | 获取字典中key对应value,可设置默认值。若key不存在于字典中,将会添加key并将value设为默认值 |
| dict.keys() | 获取字典所有的key,返回一个视图对象。字典改变,视图也会跟着变化 |
| dict.values() | 获取字典所有的value,返回一个视图对象 |
| dict.items() | 获取字典所有的(key,value),返回一个视图对象 |
| dict.copy() | 拷贝字典 |
| dict.fromkeys(seq[,default]) | 以序列seq中元素做字典的key创建一个新字典,可设置value的默认值 |
5. 四种具体容器区别
| 数据结构 | 是否可变 | 是否重复 | 是否有序 | 定义符号 |
|---|---|---|---|---|
| 列表(List) | 可变 | 允许 | 有序 | []或list() |
| 元组(Tuple) | 不可变 | 允许 | 有序 | ()或tuple() |
| 字典(Dictionary) | 可变 | 键不允许,值允许 | 键无序(Python 3.7+版本中保持插入顺序) | {}或dict() |
| 集合(Set) | 可变 | 不允许 | 无序 | {}或set() |
第七章
命名规范总结
| 类型 | 命名规范 | 示例 |
|---|---|---|
| 类名 | PascalCase(帕斯卡命名法) | class UserService: |
| 异常类 | PascalCase + “Error”(帕斯卡命名法+Error) | class ValidationError: |
| 函数/方法 | snake_case(蛇形命名法) | def calculate_total(): |
| 变量 | snake_case(蛇形命名法) | user_count = 10 |
| 常量 | UPPER_SNAKE_CASE(蛇形命名法大写) | MAX_USERS = 100 |
| 模块名 | snake_case(蛇形命名法) | database_utils.py |
第八章 函数
1. 函数的概念
“函数” 是带名字的代码块,用于完成具体的任务,可重复使用。当需要在程序中多次执行同一项任务时,无须反复编写完成该任务的代码,只需要调用改名字的代码块即可,这就叫函数。
使用注意:
- Python中的函数必须先定义后使用
- Python提供了许多内建函数,比如print()。
- Python允许自己创建函数。
2. 函数的定义
2.1 定义函数语法
def 函数名 (参数列表) :
函数体
[return]
2.2 定义函数规则
- 函数代码块以def关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数()后面以冒号结束。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数体开始缩进
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None
2.3 函数名
- 函数名一般是一个动词,第一个单词小写,其他每个单词的首字母大写。
2.4 参数
- 函数在完成某个功能时,可能需要一些数据,在定义函数时指定函数参数来接收这些数据。如在屏幕上打印信息,需要把要打印的信息传递给print()函数。
- 如果有多个参数,参数之间使用逗号分隔。函数也可以没有参数,但是小括弧不能省略。
2.5 函数体
- 调用函数时执行的代码,也即要完成功能的逻辑,也可包含函数说明。
2.6 返回值
-
有些函数在执行完之后,需要把执行后的某些结果,返回值给调用者
-
通过return关键字进行返回,返回到函数调用的位置。
3.函数的入门
3.1 案例打印欢迎信息
-
不使用函数代码。
# 打印欢迎信息1 print("********************************") print("* *") print("* 欢迎来到Python世界 *") print("* *") print("********************************") # 打印欢迎信息2 print("********************************") print("* *") print("* 欢迎来到Python世界 *") print("* *") print("********************************") # 打印欢迎信息3 print("********************************") print("* *") print("* 欢迎来到Python世界 *") print("* *") print("********************************") -
使用函数改进后的代码
def print_welcome(): """打印欢迎信息""" print("********************************") print("* *") print("* 欢迎来到Python世界 *") print("* *") print("********************************") # 多次调用函数打印欢迎信息 print_welcome() print_welcome() print_welcome()
3.2 函数的好处
- 避免代码重复:相同的打印逻辑只需写一次
- 提高可读性:
print_welcome()比多行print更直观 - 便于修改:如需修改边框样式,只需改函数一处
- 减少错误:消除了复制粘贴可能带来的格式错误
- 代码组织更清晰:主程序逻辑更简洁明了
3.3 使用函数注意事项
- 函数必须先定义再调用
- 函数只有在被调用函数后,才会真正执行。
4. 函数的参数
需求:根据购物车的商品价格,计算购物车所有商品总价。
4.1 案例计算购物车总价
-
函数无参数实现
# 无参数版本 - 只能计算固定的购物车 def calculate_total_no_params(): """计算固定购物车总价""" prices = [100, 50, 30] # 商品价格固定写死在函数内 total = 0 for price in prices: total += price return total # 只能计算一个固定的购物车 print(f"购物车总价:{calculate_total_no_params()}") -
有参数函数的实现
# 有参数版本 - 可以计算任意购物车 def calculate_total(prices): """计算任意购物车总价""" total = 0 for price in prices: total += price return total # 可以计算任意购物车 cart1 = [100, 50, 30] cart2 = [200, 80, 45, 60] cart3 = [75, 90, 120] print(f"第一个购物车总价:{calculate_total(cart1)}:") print(f"第二个购物车总价:{calculate_total(cart2)}") print(f"第三个购物车总价:{calculate_total(cart3)}")
4.2 函数参数的好处
1. 代码复用:同一函数可以处理不同数据
2. 灵活性高:不修改函数就能适应新需求
3.职责清晰: 函数只负责计算,数据由外部提供
4. 代码复用:同一函数可以处理不同数据,数据与逻辑分离
4.3 形参和实参
- 在定义函数时,指定的参数称为形式参数,简称为形参
- 在调用函数时,给函数传递的参数称为实际参数,简称为实参
- 在定义函数时,形参没有分配存储空间,也没有值,仅相当于一个占位符;
- 在调用函数时, 在栈区中给函数分配存储空间, 然后给形参分配存储空间,接收调用函数式传入的实际数据。
- 函数执行结束,函数所占的栈空间会被释放,函数的形参也最终被释放。
4.4 参数的传递
1. 参数类型
在Python函数的参数传递,主要分参数类型:
- 不可变类型
- 可变类型
不可变类型:
也即值传递,如整数、浮点数、字符串、元组等。比如如def fun(a):,传递的只是a的值,没有传递a对象的地址。比如在fun(a)内部修改a的值,只是修改另一个复制的对象的值,不会影响 a 本身对象的值。
可变类型:
也即引用(地址)传递,如列表,集合、字典等。如def fun(la):,则是将 la对象地址传过去,修改后fun外部的la也会受影响。
其实Python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
2. 传入不可变对象类型参数案例
'''
该案例演示了Python函数传递不可变对象
'''
def change_int(a) :
print("函数体中未改变前a的内存地址",id(a))
a = 10
print("函数体中改变后a的内存地址",id(a))
a = 2
changeInt(a)
print(a)
print("函数外a的内存地址",id(a))
# 输出结果
# 函数体中未改变前a的内存地址 140729722661336
# 函数体中改变后a的内存地址 140729722661592
# 2
# 函数外a的内存地址 140729722661336
说明:实例中有 int 对象 2,指向它的变量是a,在传递给 change_int 函数时,按传值的方式复制了变量 a,a 和 a 都指向了同一个 int 对象,函数外a的内存地址和未改变前a的地址是相同的。在内部执行 a=10 时,新生成一个 int 值对象 10,并让 a 指向它。这个时候内部a的内存地址也即发生了改变。
3. 传入可变对象类型参数案例
'''
该案例演示了Python函数传递可变对象
'''
def change_list(myList) :
myList[1] = 50
print("函数内的值",myList)
print("函数内列表的内存",id(myList))
mlist = [1,2,3]
change_list(mlist)
print("函数外的值",mlist)
print("函数外列表的内存",id(mlist))
# 输出结果
# 函数内的值 [1, 50, 3]
# 函数内列表的内存 1380193079680
# 函数外的值 [1, 50, 3]
# 函数外列表的内存 1380193079680
说明:可变对象在函数里修改了参数,那么在函数外面,这个原始的参数也被改变了。
通过内存地址的输出,我们可以看出来,是在原有的列表对象上进行的修改。
4.5 函数可使用的参数形式
1. 必须参数
调用函数时传入参数的的数量必须和定义函数时参数一致。
'''
该案例演示了函数位置实参
'''
def func(a, b, c):
print(a, b, c)
func(1, 2, 3) # 1 2 3
2. 关键字参数
函数调用使用关键字参数和定义方法形参名进行匹配,并且不要求函数调用时关键字参数的顺序与定义函数的参数名一致。
'''
该案例演示了函数调用时的关键字参数
'''
def print_info(name,age) :
print("姓名:",name)
print("年龄:",age)
# Python解释器可以通过age和name这样的关键字去和形参进行匹配
print_info(name = "zhangsan",age = 18)
print_info(age = 18,name = "zhangsan")
3. 默认值参数
定义函数时,可以给每个形参指定默认值,接着在调用函数时,可以选择是否给提供了实参值,如果提供了,则使用提供的实参值,否则使用形参的默认值,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用。
'''
该案例演示了函数调用时的默认参数
'''
def print_info(name,age = 20) :
print("姓名:",name)
print("年龄:",age)
print_info("zhangsan")
print_info("lisi",30)
4. 不定长参数
函数定义的时候,参数的个数是不确定的。
一种是参数带一个星号*的可变长参数
语法:
def 函数名([普通参数,] *var_args_tuple ):
函数体
说明:
- 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
- 如果形参中出现了不定长参数,那么在调用函数的时候,先通过位置进行必须参数的匹配,然后匹配不定长参数。
- 如果不定长的参数后面还有参数,必须通过关键字参数传参。
'''
该案例演示了函数调用时的不定长参数
'''
def print_info(num,*vartuple):
print(num)
print(vartuple)
printInfo(70,60,50)
print("-" * 20)
# 如果不定长的参数后面还有参数,必须通过关键字参数传参
def print_info(num1,*vartuple,num) :
print(num)
print(num1)
print(vartuple)
print_info(10,20,num = 40)
print("-" * 20)
# 如果没有给不定长的参数传参,那么得到的是空元组
print_info(70,num = 60)
还有一种就是参数带两个星号的可变长参数,基本语法如下:
语法:
def 函数名([普通参数,] **var_args_dict ):
函数体
说明:
- 加了两个星号 ** 的参数会以字典的形式导入
- 后面就不能再有其他参数了
'''
该案例演示了函数调用时的不定长参数
'''
def print_info(num,**vardict):
print(num)
print(vardict)
# return
print_info(10,key1 = 20,key2 = 30)
在程序开发中,有时候希望一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理。返回值就是函数完成工作后,给调用者的一个结果。
说明:
- 在函数中使用 return 关键字可以返回结果 ,并结束正在执行的函数
- 如果return后面跟[表达式],在结束函数的时,返回计算完表达式结果。
- 如果仅仅是return关键字,后面没有加内容,函数执行返回调用方None。
- 调用函数一方,可以使用变量来接收函数的返回结果
- return 语句可以返回多个值,多个值会放在一个元组中。
案例:
# 1.返回布尔表达式
def is_even(num):
"""检查数字是否为偶数"""
return num % 2 == 0 # 返回布尔表达式的结果
print(is_even(4)) # 输出: True
print(is_even(5)) # 输出: False
# 2.不带表达式的 return 语句,返回 None。
def f(a, b, c):
pass
return
print(f(1, 2, 3)) # None
# 3.函数中如果没有 return 语句,在函数运行结束后也会返回 None。
def f(a, b, c):
pass
print(f(1, 2, 3)) # None
# 4.用变量接收返回结果
def add(num1,num2) :
'''求两个数的和'''
sum1 = num1 + num2
return sum1
res = add(10,20)
print("两个数的和为:" ,res)
# 5.return 语句可以返回多个值,多个值会放在一个元组中。
def f(a, b, c):
return a, b, c, [a, b, c]
print(f(1, 2, 3)) # (1, 2, 3, [1, 2, 3])
6. 函数嵌套调用
在一个函数中调用另一个函数,当内层函数执行完之后才会继续执行外层函数。
def function_A():
print("\t函数 A 开始执行")
print("\t函数 A 执行中...")
print("\t函数 A 结束执行")
def function_B():
print("函数 B 开始执行")
print("函数 B 执行中...")
function_A()
print("函数 B 执行中...")
print("函数 B 结束执行")
function_B()
#输出结果:
#函数 B 开始执行
#函数 B 执行中...
#函数 A 开始执行
#函数 A 执行中...
#函数 A 结束执行
#函数 B 执行中...
#函数 B 结束执行
7. 变量的作用域
Python中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了哪一部分程序可以访问哪个变量,Python的作用域一共有4种,分别是:
L (Local) 局部作用域
E (Enclosing)嵌套作用域
G (Global) 全局作用域
B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部,再找不到就会去全局找,再者去内建中找。以下案例演示各种作用域类型。
7.1 局部作用域
def local_scope():
x = 10 # 局部变量
print("局部作用域内:", x)
local_scope()
print(x) # 报错 NameError: name 'x' is not defined
说明:
x只能在local_scope()函数内部访问- 函数执行完毕后局部变量会被销毁
7.2 嵌套作用域(Enclosing)
def outer():
y = 20 # 嵌套作用域变量
def inner():
print("内部函数访问嵌套作用域变量:", y) # 可以访问外部函数的变量
inner()
print("外部函数访问自己的变量:", y)
outer()
# print(y) # 报错 NameError
说明:
inner()可以访问outer()的变量y- 外部无法直接访问嵌套作用域的变量
7.3 全局作用域(Global)
z = 30 # 全局变量
def access_global():
print("函数内访问全局变量:", z)
access_global()
print("直接访问全局变量:", z)
8. 全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的变量拥有全局作用域。局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。
'''
该案例演示了全局变量和局部变量
'''
number_sum = 0 # 这是一个全局变量
def add(num1,num2) :
sum = num1 + num2 # 这是一个局部变量
print("函数内局部变量的值:",sum)
return sum
add(10,20)
print(num1) # num1访问不到
print("函数外全局变量:",number_sum)
8.1 global关键字
问题:定义了一个全局变量,如何在函数内对其进行修改?
# 1.全局变量是不可变类型
var1 = 100
def immutable_a():
var1 = 200
print("var1:", var1)
print(var1) # 100
immutable_a() # var1: 200
print(var1) # 100
# 2.全局变量是不可变类型
list1 = [1, 2, 3]
def mutable_b():
list1[0] = -1000
print("list1:", list1)
print(list1) # [1, 2, 3]
mutable_b() # list1: [-1000, 2, 3]
print(list1) # [-1000, 2, 3]
通过上述,你发现对于全局变量是不可变类型,似乎在函数内无法修改,而全局变量是可变类型,则可以直接修改。
这是因为当你尝试修改不可变对象时,Python 实际上会创建一个新对象并重新赋值。而针对去修改可变对象时,Python会选择性的创建新对象,也就是说可能会创建,也可以不创建,直接修改原来对象。
针对immutable_a()函数的 var1=200,只是修改新对象的值。并没有修改原来对象的值。所以immutable_a()函数调用完,输出的var1仍然是原来的100,不是修改后的200.
针对mutable_b()中通过 list1[0] 这种方式修改时,Python 会先查找 list1 这个变量名。按照 LEGB 规则,它会找到全局的 list1,然后直接操作该对象的内容。所以函数调用完,在外部输出的list1,就是函数内部修改后的最新值。
那么在回到不可变类型上,如果现在就想在方法内部修改不可变类型,该怎么办呢。
global是Python中用于声明全局变量的关键字,它允许在函数内部修改和访问在全局作用域(模块级别)定义的变量。
我们来改进上述代码片段的immutable_a()方法
# 1.全局变量是不可变类型
var1 = 100
def immutable_a():
global var1
var1 = 200
print("var1:", var1)
print(var1) # 100
immutable_a() # var1: 200
print(var1) # 200
当我们在immutable_a()方法中对var1变量添加了global关键字之后,发现immutable_a()调用完,全局变量的var1值也变为修改后的200,这就是global的作用。
那么在 immutable_a()中对var1添加了global关键字之后,发生了什么呢,其实底层就是明确告诉 Python 要修改的是全局变量(修改原来对象),而不是创建局部变量(创建新对象)。
在回到尝试修改不可变对象,我们刚刚说当修改不可变对象时Python会试选择性的创建新对象。为什么说可能,因为如果我们只是修改可变类型的部分内容, Python 不会将其视为局部变量,也即修改的还是全局变量。但是如果修改可变类型的全部内容,比如重新赋值,那么Python依然会创建一个新的局部变量。比如针对mutable_b()函数做一次修改。
改进上述代码片段的mutable_b()函数
list1 = [1, 2, 3]
def mutable_b():
list1 = [-1000, 2, 3]
print("list1:", list1)
print(list1) # [1, 2, 3]
mutable_b() # list1: [-1000, 2, 3]
print(list1) # [1, 2, 3]
输入结果和修改不可变类型一样,依然没有修改全局变量,局么我们对局部变量list1添加global,在看结果。
list1 = [1, 2, 3]
def mutable_b():
global list1
list1 = [-1000, 2, 3]
print("list1:", list1)
print(list1) # [1, 2, 3]
mutable_b() # list1: [-1000, 2, 3]
print(list1) # [-1000, 2, 3]
成功修改了全局变量。
Global小总结:
| 行为 | 不可变类型 (int, str, tuple 等) | 可变类型 (list, dict, set 等) |
|---|---|---|
| 修改变量值 | 必须用 global(因为创建新对象) | 不需要 global(直接修改内容) |
| 重新赋值 | var1 = 200(需要 global) | list1 = [-1000,2,3](需要 global) |
| 修改内部内容 | 不适用(不可变对象无法修改内容) | list1[0] = -1000(不需要 global) |
8.2 nonlocal关键字
nonlocal 也用作内部作用域修改外部作用域的变量的场景,不过此时外部作用域不是全局作用域而是嵌套作用域。
def function_outer():
var1 = 1
print(var1)
def function_inner():
nonlocal var1
var1 = 200
function_inner()
print(var1)
function_outer() # var1: 1 -> 200
Global和nonlocal的区别
global:用于访问和修改全局(模块级)变量nonlocal:用于访问和修改嵌套作用域中的变量
9. 匿名函数
Python使用 lambda 来定义匿名函数,所谓匿名,指其不用 def 的标准形式定义函数。
语法:
lambda 参数列表: 表达式
说明:
- lambda 只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块,所以仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda有自己的参数列表作为局部变量
- 可以访问全局变量
- 访问外围函数的局部变量
- 不能访问未定义的变量
案例:
9.1 使用普通函数传参
def operator(a, b):
return a + b
def function(a, b, abc):
return abc(a, b)
print(function(1, 2, operator))
9.2 使用匿名函数传参
def function(a, b, operator):
return operator(a, b)
print(function(1, 2, lambda x, y: x + y))
9.3 匿名函数作为内置函数的参数
可以将匿名函数与常用的内置参数搭配使用。
1)sorted()
有三名学生的姓名和年龄,按年龄排序。
student_list = [{"name": "zhang3", "age": 36}, {"name": "li4", "age": 14}, {"name": "wang5", "age": 27}]
print(sorted(student_list, key=lambda x: x["age"]))
2) map()
map() 的主要作用是将给定的函数批量应用到可迭代对象的每个元素上,实现数据转换
map_result = map(lambda x: x * x, [0, 1, 3, 7, 9])
print(list(map_result)) # [0, 1, 9, 49, 81]
3)filter()
filter 的主要作用是将给定的函数批量应用到可迭代对象的每个元素上,实现数据过滤
filter_result = filter(lambda x: x >= 0, [0, -1, -3, 7, 9])
print(list(filter_result)) # [0, 7, 9]
第九章 文件操作
1、文件的基本概念
在计算机中,文件是存储在磁盘上的数据集合。文件可以包含各种类型的数据,如文本、图像、音频、视频或程序代码。
文件系统通过文件名和文件路径来定位和管理文件。文件名通常包含文件的名称和扩展名,扩展名用于表示文件的类型(例如 .txt 表示文本文件,.jpg 表示图像文件)。文件路径可以是绝对路径(从文件系统的根目录开始)或相对路径(相对于当前工作目录)。
在编写程序的时候,数据是以二进制的形式存储在内存的,将数据写到磁盘文件的过程称之为持久化。
2、文件的分类
1)纯文本文件
有统一的编码,可以被看做存储在磁盘上的长字符串。
纯文本文件编码格式常见的有ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16等。
2)二进制文件
没有统一的字符编码,直接由0与1组成。
如图片文件(jpg、png),视频文件(avi)等。
3、文件的路径
1)相对路径
从当前位置到指定位置的路径。
如:./hello_world.py
./代表当前路径。…/代表上一级路径。
2)绝对路径
从根目录到指定位置的路径。
如:E:/Hello/hello.py
4 文件的打开与关闭
1)打开文件
使用 open() 打开或创建文件,该方法执行完毕之后返回的是一个file对象。
1.1)常用形式
open(文件名, 模式)
f = open("test.txt", "w")
1.2 常用文件操作模式
| 模式 | 说明 |
|---|---|
| r | 读写方式:只读,文件若不存在会报错。默认此模式 |
| w | 读写方式:写入,写入前清空原有数据。文件不存在会创建文件 |
| a | 读写方式:追加写入,在原有数据后追加,文件不存在会创建文件 |
| x | 读写方式:创建新文件并写入,文件若已存在会报错 |
| b | 编码方式:以二进制打开。一般用于非文本文件如图片等 |
| t | 编码方式:以文本模式打开,默认此模式 |
2)关闭文件
f.close()
5. 文件读写
5.1 写数据
# 打开文件
f = open("test.txt", "w")
# 写入数据
f.write("hello world\n")
f.write("nihao python\n")
# 关闭文件
f.close()
5.2 读数据
1)read
read([size]) 可以从文件中读取数据,size 表示要从文件中读取的数据的长度(单位是字节),如果没有传入 size 则读取文件中所有的数据。
# 打开文件
f = open("test.txt", "rt")
# 读取文件所有数据
print(f.read())
# 关闭文件
f.close()
print("-"*20)
f = open("test.txt", "rt")
# 读取文件5个字节数据
print(f.read(5))
print(f.read(8))
f.close()
- readline
readline([size]) 可以从文件中读取整行数据,也可以通过 size 设置读取数据的长度。
# 打开文件
f = open("test.txt", "rt")
print(f.readline()) # 读取一行(直到遇到换行符\n或文件结尾)
print(f.readline(1)) # 读取最多1个字节/字符(不是一行)
print(f.readline(1)) # 继续读取下一个字符
# 关闭文件
f.close()
- readlines
readlines([size]) 读取所有行并返回列表,若给定 size>0,它会尝试读取大约 size 字节的行(可能比 size 大,因为必须读取完整的行)。
假设 english.txt 内容:
Line 1
Second line is longer
Third lie
Last line
代码
f = open('english.txt', 'r')
try:
# 尝试读取约10字节
chunk = f.readlines(10)
finally:
f.close() # 确保文件一定会被关闭
输出结果:(尽管超过了10字节,但必须读取完整行)
读取约10字节,实际读取: ['Line 1\n', 'Second line is longer\n']
6. 文件的常用函数
| 函数 | 说明 |
|---|---|
| file.seek(offset[,from]) | 移动偏移量并返回新的绝对位置。 offset:移动的字节数,如果是负数表示从倒数第几位开始。 from:从哪个位置开始移动;0为开头,1为当前位置,2为末尾。from不为0时需使用’b’二进制模式打开文件。 |
| file.tell() | 返回当前偏移量 |
| file.truncate([size]) | 从开头开始截断文件为size个字符,无size表示从当前位置截断。windows系统下的换行大小2个字符 |
| file.writelines(seq) | 将序列中字符串写入文件,需要自己加入换行符 |
| file.readable() | 如果可以读取文件则返回True |
| file.writeable() | 如果可以写入文件则返回True |
| file.seekable() | 如果文件支持随机访问则返回True |
| os.rename(old,new) | 重命名文件 |
| os.remove(file) | 删除文件 |
| os.mkdir(dir) | 创建目录,不支持递归创建 |
| os.makedirs(dir) | 递归创建目录 |
| os.getcwd() | 获取当前路径 |
| os.chdir(dir) | 进入指定目录 |
| os.listdir(dir) | 获取目录下文件和目录列表 |
| os.rmdir(dir) | 删除空目录 |
| os.removedirs(dir) | 递归删除空目录 |
| os.path.abspath(path) | 将相对路径转换为绝对路径 |
| os.path.basename(path) | 获取路径中的文件名部分 |
| os.path.dirname(path) | 获取路径中的目录部分 |
| os.path.join(*paths) | 拼接多个路径,自动处理路径分隔符 |
| os.path.split(path) | 将路径分割为目录和文件名的元组 |
| os.path.splitext(path) | 将路径分割为文件名和扩展名的元组 |
| os.path.exists(path) | 判断路径是否存在 |
| os.path.isfile(path) | 判断路径是否为文件 |
| os.path.isdir(path) | 判断路径是否为目 |
| os.path.getsize(path) | 获取文件的大小,以字节为单位 |
| os.path.getatime(path) | 获取文件的最后访问时间 |
| os.path.getmtime(path) | 获取文件的最后修改时间 |
| os.walk()递归遍历目录 | 递归遍历目录 |
(1)案例:递归遍历当前路径下所有目录和文件
使用os.walk()递归遍历目录树
import os
for root, dirs, files in os.walk(os.getcwd()):
print("当前工作目录的路径:", root)
print("当前路径下的子目录:", dirs)
print("当前路径下的所有文件:", files)
print()
(2)案例:文件拷贝
def copy_file(source_file_path,dest_file__path):
# 打开源文件
source_file = open(source_file_path, 'rb')
# 读取源文件中的内容
content = source_file.read()
# 打开目的地文件
dest_file = open(dest_file__path, 'wb')
# 将读取到的数据写入到目的地
dest_file.write(content)
# 关闭源文件
source_file.close()
# 关闭目的地文件
dest_file.close()
copy_file("D:/tmp/1.jpg","D:/1.jpg")
优化:
def copy_file(source_file, dest_file):
# 打开文件
source = open(source_file, "rb")
context = source.read(1024)
target = open(dest_file, "wb")
while context:
target.write(context)
context = source.read(1024)
else:
print("复制完成")
source.close()
# 关闭资源
# source.close()
target.close()
copy_file("D:/tmp/1.jpg", "D:/1.jpg")
更多推荐


所有评论(0)