Appearance
Python
导航目录
- 第 1 章 基础知识
- 第 2 章 流程控制语句
- 第 3 章 容器数据类型
- 第 4 章 函数
- 第 5 章 文件操作
- 第 6 章 面向对象之类和对象
- 第 7 章 面向对象之三大特性
- 第 9 章 错误和异常
- 第 10 章 模块与包
- 第 11 章 Python高级语法
- 第 12 章 进程与线程
- 第 13 章 网络编程
- 第 14 章 正则表达式
第 1 章 基础知识
1.1 注释
Python 中的注释用于解释代码的功能和逻辑,提高代码可读性。Python 支持两种注释方式:
单行注释:使用
#符号,从#开始到行尾的内容均为注释。python# 这是一行单行注释 print("Hello, World!") # 行尾注释多行注释:使用三引号(单引号或双引号)包裹多行内容,通常用于函数或类的文档说明。
python""" 这是一个多行注释 可以跨越多行 通常用于函数或类的文档说明 """ def example(): """这是函数的文档字符串""" pass
1.2 变量与常量
变量是存储数据的容器,Python 是动态类型语言,无需显式声明变量类型,变量类型会根据赋值自动推导。
变量命名规则:
- 变量名只能包含字母、数字和下划线(
_)。 - 变量名不能以数字开头。
- 变量名区分大小写(
name和Name是不同变量)。 - 避免使用 Python 关键字(如
if、for、while等)作为变量名。
- 变量名只能包含字母、数字和下划线(
变量赋值:
python# 基本赋值 age = 18 # 整数 name = "Alice" # 字符串 is_student = True # 布尔值 # 多变量赋值 a, b, c = 1, 2, 3 # 变量交换 x, y = 10, 20 print(f"交换前: x={x}, y={y}") x, y = y, x print(f"交换后: x={x}, y={y}")常量:在 Python 中,通常使用全大写字母来表示常量,虽然 Python 不会强制限制常量的值被修改,但这是一种约定俗成的规范。
python# 常量定义 PI = 3.1415926535 MAX_AGE = 120 # 虽然可以修改,但不推荐 # PI = 3.14 # 不推荐修改常量值
1.3 数据类型
Python 内置的数据类型主要包括:
1.3.1 数据类型分类
- 按可变性分类:
- 不可变数据类型:一旦创建,值不能修改,包括
int、float、complex、str、bool、tuple。(数值\字符串\元组) - 可变数据类型:创建后可以修改其值,包括
list、dict、set。
- 不可变数据类型:一旦创建,值不能修改,包括
1.3.1.1 可变与不可变数据类型案例
不可变数据类型案例:
- 字符串:字符串是不可变的,尝试修改字符串会创建一个新的字符串对象,而不是修改已有的字符串。
- 整数:整数是不可变的,尝试修改整数会创建一个新的整数对象,而不是修改已有的整数。
- 元组:元组是不可变的,尝试修改元组会引发错误。
python
# 字符串是不可变的
s = "hello"
print(f"原始字符串: {s}, id: {id(s)}")
# 尝试修改字符串(实际上会创建新对象)
s = s + " world"
print(f"修改后字符串: {s}, id: {id(s)}") # 注意id不同
# 整数是不可变的
x = 10
print(f"原始整数: {x}, id: {id(x)}")
# 尝试修改整数(实际上会创建新对象)
x = x + 5
print(f"修改后整数: {x}, id: {id(x)}") # 注意id不同
# 元组是不可变的
t = (1, 2, 3)
print(f"原始元组: {t}, id: {id(t)}")
# 尝试修改元组会引发错误
# t[0] = 4 # 会引发 TypeError: 'tuple' object does not support item assignment可变数据类型案例:
python
# 列表是可变的
lst = [1, 2, 3]
print(f"原始列表: {lst}, id: {id(lst)}")
# 修改列表(直接修改对象,不创建新对象)
lst.append(4)
print(f"修改后列表: {lst}, id: {id(lst)}") # id相同
lst[0] = 10
print(f"再次修改后列表: {lst}, id: {id(lst)}") # id相同
# 字典是可变的
d = {"name": "Alice", "age": 18}
print(f"原始字典: {d}, id: {id(d)}")
# 修改字典(直接修改对象,不创建新对象)
d["age"] = 19
print(f"修改后字典: {d}, id: {id(d)}") # id相同
d["city"] = "New York"
print(f"再次修改后字典: {d}, id: {id(d)}") # id相同
# 集合是可变的
s = {1, 2, 3}
print(f"原始集合: {s}, id: {id(s)}")
# 修改集合(直接修改对象,不创建新对象)
s.add(4)
print(f"修改后集合: {s}, id: {id(s)}") # id相同
s.remove(1)
print(f"再次修改后集合: {s}, id: {id(s)}") # id相同内存地址比较:
python
# 不可变类型 - 每次修改都会创建新对象
x = 10
y = x
print(f"x: {x}, id(x): {id(x)}")
print(f"y: {y}, id(y): {id(y)}")
print(f"x is y: {x is y}") # True,指向同一个对象
x = x + 1
print(f"x: {x}, id(x): {id(x)}") # id改变
print(f"y: {y}, id(y): {id(y)}") # id不变
print(f"x is y: {x is y}") # False,指向不同对象
# 可变类型 - 修改不会创建新对象
lst1 = [1, 2, 3]
lst2 = lst1
print(f"lst1: {lst1}, id(lst1): {id(lst1)}")
print(f"lst2: {lst2}, id(lst2): {id(lst2)}")
print(f"lst1 is lst2: {lst1 is lst2}") # True,指向同一个对象
lst1.append(4)
print(f"lst1: {lst1}, id(lst1): {id(lst1)}") # id不变
print(f"lst2: {lst2}, id(lst2): {id(lst2)}") # id不变,且值也被修改了
print(f"lst1 is lst2: {lst1 is lst2}") # True,仍然指向同一个对象数值类型:
int(整数):如10、-5、0。float(浮点数):如3.14、-0.5。complex(复数):如1+2j。Decimal(十进制浮点数):用于高精度数值计算,需要从decimal模块导入。
python# Decimal 类型示例 from decimal import Decimal # 创建 Decimal 对象 num1 = Decimal('0.1') num2 = Decimal('0.2') # 精确计算 print(num1 + num2) # 输出: 0.3 # 与 float 比较 print(0.1 + 0.2) # 输出: 0.30000000000000004字符串(
str):由字符组成的序列,使用单引号、双引号或三引号包裹。pythons1 = 'Hello' # 单引号字符串 s2 = "World" # 双引号字符串 s3 = '''Multi-line string''' # 三引号多行字符串 # 字符串操作示例 print(s1 + " " + s2) # 字符串拼接 print(s1 * 3) # 字符串重复 print(len(s1)) # 字符串长度布尔值(
bool):只有True和False两个值,用于逻辑判断。列表(
list):有序、可变的元素集合,使用方括号[]定义。pythonfruits = ["apple", "banana", "cherry"] print(f"原始列表: {fruits}") fruits.append("orange") # 添加元素 print(f"添加后: {fruits}") fruits[0] = "grape" # 修改元素 print(f"修改后: {fruits}") del fruits[1] # 删除元素 print(f"删除后: {fruits}")元组(
tuple):有序、不可变的元素集合,使用圆括号()定义。pythoncoordinates = (10, 20) print(f"元组: {coordinates}") print(f"第一个元素: {coordinates[0]}") # coordinates[0] = 30 # 尝试修改元组会引发错误字典(
dict):键值对的无序集合,使用大括号{}定义。pythonperson = {"name": "Bob", "age": 25, "city": "New York"} print(f"字典: {person}") print(f"姓名: {person['name']}") person["age"] = 26 # 修改值 print(f"修改年龄后: {person}") person["occupation"] = "Engineer" # 添加新键值对 print(f"添加职业后: {person}")集合(
set):无序、无重复元素的集合,使用大括号{}或set()函数定义。pythonunique_numbers = {1, 2, 3, 4, 4, 5} # 自动去重 print(f"集合: {unique_numbers}") # 集合操作 unique_numbers.add(6) # 添加元素 print(f"添加后: {unique_numbers}") unique_numbers.remove(2) # 删除元素 print(f"删除后: {unique_numbers}")
1.3.2 类型判断
使用 type() 函数可以判断一个变量的类型:
python
# 类型判断示例
age = 18
name = "Alice"
is_student = True
fruits = ["apple", "banana"]
print(type(age)) # <class 'int'>
print(type(name)) # <class 'str'>
print(type(is_student)) # <class 'bool'>
print(type(fruits)) # <class 'list'>
# 使用 isinstance() 函数判断是否为指定类型
print(isinstance(age, int)) # True
print(isinstance(name, str)) # True
print(isinstance(fruits, list)) # True1.3.3 小整数池和大整数池
Python 为了提高性能,对于一些常用的整数,会提前创建对象并缓存起来,这就是小整数池。
小整数池:Python 会缓存
-5到256之间的整数,这些整数在内存中只有一个实例。python# 小整数池示例 a = 100 b = 100 print(a is b) # True,指向同一个对象 c = 256 d = 256 print(c is d) # True,指向同一个对象 e = 257 f = 257 print(e is f) # False,不同的对象大整数池:对于大于 256 或小于 -5 的整数,Python 不会缓存,但在同一代码块中,如果值相同,可能会复用对象。
python# 大整数池示例 def test(): x = 1000 y = 1000 print(x is y) # True,同一代码块中 test() # 输出 True a = 1000 b = 1000 print(a is b) # False,不同代码块
关于Python解释器的编译优化:常量折叠
Python 解释器会做一个编译优化,称为常量折叠:当发现两个完全一样的数字常量时,只创建一个对象,让两个变量都指向它。
测试 1:在 Python 交互终端(>>>)里分行输入
python>>> num1 = 11119999 >>> num2 = 11119999 >>> print(num1 is num2) False # 这里就是正常结果!测试 2:在同一代码块中定义
pythonnum1 = 11119999 num2 = 11119999 print(num1 is num2) # 可能会返回 True(由于常量折叠优化)
1.3.4 数据类型转换
Python 提供了多种内置函数用于数据类型转换,包括自动类型转换和强制类型转换。
1.3.4.1 自动类型转换
自动类型转换是指 Python 在运算过程中自动进行的类型转换,无需手动干预。
python
# 自动类型转换示例
# 整数与浮点数运算,结果自动转换为浮点数
num1 = 10 # int
num2 = 3.14 # float
result = num1 + num2
print(f"结果: {result}, 类型: {type(result)}") # 结果: 13.14, 类型: <class 'float'>
# 整数与复数运算,结果自动转换为复数
num3 = 5 # int
num4 = 2 + 3j # complex
result2 = num3 + num4
print(f"结果: {result2}, 类型: {type(result2)}") # 结果: (7+3j), 类型: <class 'complex'>
# 布尔值与整数运算,True 转换为 1,False 转换为 0
bool_val = True # bool
num5 = 5 # int
result3 = bool_val + num5
print(f"结果: {result3}, 类型: {type(result3)}") # 结果: 6, 类型: <class 'int'>1.3.4.2 强制类型转换
强制类型转换(强转换)是指使用内置函数手动将一种类型转换为另一种类型。
数值类型转换:
python# 整数转换 num_str = "123" num_int = int(num_str) print(f"字符串转整数: {num_int}, 类型: {type(num_int)}") # 浮点数转换 num_float = float(num_str) print(f"字符串转浮点数: {num_float}, 类型: {type(num_float)}") # 复数转换 num_complex = complex(1, 2) print(f"创建复数: {num_complex}, 类型: {type(num_complex)}")字符串转换:
python# 其他类型转字符串 num = 123 num_str = str(num) print(f"整数转字符串: {num_str}, 类型: {type(num_str)}") lst = [1, 2, 3] lst_str = str(lst) print(f"列表转字符串: {lst_str}, 类型: {type(lst_str)}")布尔值转换:
python# 其他类型转布尔值 # 以下值会被转换为 False:0, 0.0, "", [], {}, set(), None # 其他值会被转换为 True print(bool(0)) # False print(bool(1)) # True print(bool("")) # False print(bool("hello")) # True print(bool([])) # False print(bool([1, 2, 3])) # True print(bool(None)) # False容器类型转换:
python# 列表转元组 lst = [1, 2, 3] tpl = tuple(lst) print(f"列表转元组: {tpl}, 类型: {type(tpl)}") # 元组转列表 tpl = (4, 5, 6) lst = list(tpl) print(f"元组转列表: {lst}, 类型: {type(lst)}") # 列表转集合 lst = [1, 2, 2, 3, 3, 3] s = set(lst) print(f"列表转集合: {s}, 类型: {type(s)}") # 字典相关转换 d = {"a": 1, "b": 2} print(f"字典键转列表: {list(d.keys())}") print(f"字典值转列表: {list(d.values())}") print(f"字典项转列表: {list(d.items())}")进制转换:
python# 十进制转二进制 print(bin(10)) # 0b1010 # 十进制转八进制 print(oct(10)) # 0o12 # 十进制转十六进制 print(hex(10)) # 0xa # 其他进制转十进制 print(int("1010", 2)) # 10 (二进制转十进制) print(int("12", 8)) # 10 (八进制转十进制) print(int("a", 16)) # 10 (十六进制转十进制)
1.4 输入与输出
1.4.1 输出
输出是将程序处理结果展示给用户的过程,Python 中使用 print() 函数打印内容到控制台。
1.4.1.1 基本输出
python
# 打印字符串
print("Hello, Python!")
# 打印表达式结果
print(1 + 2)
# 打印多个值
name = "Alice"
age = 18
print("Name:", name, "Age:", age)
# 控制输出结束符(默认是换行符)
print("Hello", end=" ")
print("World") # 输出: Hello World1.4.1.2 格式化输出
Python 提供了多种格式化输出的方法:
- 使用
%占位符
python
num1 = 10
num2 = 3.14
# 格式化字符串
str1 = "num1 = %d, num2 = %.3f" % (num1, num2)
print(str1) # 输出: num1 = 10, num2 = 3.140- 使用
str.format()方法
python
# 不设置指定位置,按默认顺序
str2 = "num1 = {}, num2 = {}".format(num1, num2)
print(str2) # 输出: num1 = 10, num2 = 3.14
# 设置指定位置
str3 = "num2 = {1}, num1 = {0}".format(num1, num2)
print(str3) # 输出: num2 = 3.14, num1 = 10
# 设置参数名
str4 = "num1 = {n1}, num2 = {n2}".format(n1=num1, n2=num2)
print(str4) # 输出: num1 = 10, num2 = 3.14- 数字格式化
python
float1 = 31415.9
# 居中对齐,总宽度20,保留2位小数,千位分隔符
str5 = "{:*^20,.2f}".format(float1)
print(str5) # 输出: *****31,415.90******
# 左对齐
str6 = "{:*<20,.2f}".format(float1)
print(str6) # 输出: 31,415.90*********
# 右对齐
str7 = "{:*>20,.2f}".format(float1)
print(str7) # 输出: *********31,415.90- 使用大括号
{}转义
python
# 显示大括号
print("{} 对应的位置是 {{0}}".format("hello")) # 输出: hello 对应的位置是 {0}- 使用 f-字符串(Python 3.6+)
python
# 基本用法
str8 = f"num1 = {num1}, num2 = {num2}"
print(str8) # 输出: num1 = 10, num2 = 3.14
# 使用变量名作为输出的一部分(Python 3.8+)
str9 = f"{num1=}, {num2=}"
print(str9) # 输出: num1=10, num2=3.141.4.2 输入
输入是从用户获取数据的过程,Python 中使用 input() 函数获取用户输入,返回值为字符串类型。
python
# 获取字符串输入
name = input("请输入你的名字:")
print(f"你好,{name}!")
# 获取数值输入(需要类型转换)
age = int(input("请输入你的年龄:"))
print(f"你 {age} 岁了。")
# 获取浮点数输入
height = float(input("请输入你的身高(米):"))
print(f"你的身高是 {height} 米。")
# 处理输入异常
try:
age = int(input("请输入你的年龄:"))
print(f"你 {age} 岁了。")
except ValueError:
print("请输入有效的整数!")1.5 运算符
Python 支持多种运算符:
算术运算符:
+(加)、-(减)、*(乘)、/(除)、//(整除)、%(取余)、**(幂)。pythonprint(10 + 5) # 15 print(10 / 3) # 3.333... print(10 // 3) # 3 print(10 % 3) # 1 print(2 ** 3) # 8比较运算符:
==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。pythona = 10 b = 5 print(a == b) # False print(a > b) # True print(a <= b) # False逻辑运算符:
and(与)、or(或)、not(非)。pythonx = True y = False print(x and y) # False print(x or y) # True print(not x) # False赋值运算符:
=、+=、-=、*=、/=等。pythonnum = 10 num += 5 # 等同于 num = num + 5 print(num) # 15 num *= 2 # 等同于 num = num * 2 print(num) # 30成员运算符:
in(存在)、not in(不存在)。pythonfruits = ["apple", "banana", "cherry"] print("apple" in fruits) # True print("orange" not in fruits) # True身份运算符:
is(相同对象)、is not(不同对象)。pythona = [1, 2, 3] b = a c = [1, 2, 3] print(a is b) # True,a 和 b 指向同一个对象 print(a is c) # False,a 和 c 是不同的对象==与is的区别:==比较运算符:判断左右两边的值是否相等。is身份运算符:判断左右两边是否指向内存中的同一个地址。
python# 示例:小整数池 num1 = 1 num2 = 0 # == 比较运算符:判断==左右两边的值是不是相等 print(num1 == 1) # True print(num2 == 0) # True # is 判断左右两边是否指向内存中的同一个地址 print(num1 is 1) # True(小整数池缓存) print(num2 is 0) # True(小整数池缓存) # 示例:大整数 num3 = 1000 num4 = 1000 print(num3 == num4) # True(值相等) print(num3 is num4) # 注意:这里的结果可能因Python解释器的编译优化而不同
1.6 Python 编码规范
遵循 PEP 8 编码规范可以提高代码的可读性和一致性:
缩进:使用 4 个空格进行缩进(不要使用制表符)。
行长度:每行代码不超过 79 个字符。
空行:
- 顶级函数和类定义之间空两行。
- 类内部方法定义之间空一行。
命名规范:
- 变量和函数名:使用小写字母和下划线(snake_case)。
- 类名:使用首字母大写(CamelCase)。
- 常量:使用全大写字母和下划线。
导入:
- 导入语句放在文件顶部。
- 按标准库、第三方库、本地库的顺序分组导入。
- 每个导入语句单独一行。
python# 标准库 import os import sys # 第三方库 import numpy as np import pandas as pd # 本地库 from mymodule import utils from mymodule.config import settings注释:
- 为复杂代码添加注释。
- 使用文档字符串(docstring)为模块、函数、类添加说明。
pythondef calculate_area(radius): """计算圆的面积 Args: radius (float): 圆的半径 Returns: float: 圆的面积 """ import math return math.pi * radius ** 2
第 2 章 流程控制语句
2.1 顺序
顺序结构是最基本的流程控制结构,代码按照从上到下的顺序依次执行,没有分支或循环。
python
# 顺序结构示例
print("步骤 1: 开始")
x = 10
y = 20
z = x + y
print(f"步骤 2: 计算结果: {z}")
print("步骤 3: 结束")2.2 分支
分支结构根据条件判断来决定执行不同的代码块。Python 提供了 if 语句来实现分支结构。
2.2.1 单分支
单分支结构只有一个条件判断,当条件为真时执行相应的代码块。
python
# 单分支示例
age = 18
if age >= 18:
print("你已经成年了!")2.2.2 双分支
双分支结构包含一个条件判断和两个代码块,当条件为真时执行第一个代码块,否则执行第二个代码块。
python
# 双分支示例
age = 16
if age >= 18:
print("你已经成年了!")
else:
print("你还未成年。")2.2.3 多分支
多分支结构包含多个条件判断,按照顺序依次检查条件,执行第一个条件为真的代码块。
python
# 多分支示例
score = 85
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")2.2.4 嵌套分支
嵌套分支是在一个分支结构内部再嵌套另一个分支结构。
python
# 嵌套分支示例
age = 18
gender = "male"
if age >= 18:
print("你已经成年了!")
if gender == "male":
print("你是成年男性。")
else:
print("你是成年女性。")
else:
print("你还未成年。")2.2.5 match case语句
match case 语句(Python 3.10+)用于模式匹配,类似于其他语言中的 switch-case 语句。
python
# match case 语句示例(Python 3.10+)
def get_day_name(day):
match day:
case 1:
return "星期一"
case 2:
return "星期二"
case 3:
return "星期三"
case 4:
return "星期四"
case 5:
return "星期五"
case 6:
return "星期六"
case 7:
return "星期日"
case _:
return "无效的日期"
print(get_day_name(3)) # 输出: 星期三
print(get_day_name(8)) # 输出: 无效的日期2.2.6 三目运算符
三目运算符是一种简洁的条件表达式,用于在一行代码中实现简单的分支逻辑。
python
# 三目运算符示例
age = 18
message = "成年" if age >= 18 else "未成年"
print(message) # 输出: 成年
# 复杂一点的三目运算符
score = 75
grade = "优秀" if score >= 90 else "良好" if score >= 80 else "及格" if score >= 60 else "不及格"
print(grade) # 输出: 良好2.3 循环
循环结构用于重复执行一段代码,Python 提供了 while 和 for 两种循环语句。
2.3.1 while循环
while 循环在条件为真时重复执行代码块,直到条件为假时停止。
python
# while 循环示例
count = 1
while count <= 5:
print(f"当前计数: {count}")
count += 1
# 无限循环(需要手动中断)
# while True:
# print("这是一个无限循环")2.3.2 for循环
for 循环用于遍历可迭代对象(如列表、元组、字符串等)中的元素。
python
# for 循环示例
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(f"水果: {fruit}")
# 遍历数字范围
for i in range(5): # 0-4
print(f"数字: {i}")
# 遍历字符串
for char in "Hello":
print(f"字符: {char}")
# 遍历字典
person = {"name": "Alice", "age": 18, "city": "New York"}
for key, value in person.items():
print(f"{key}: {value}")2.3.3 continue
continue 语句用于跳过当前循环的剩余部分,直接开始下一次循环。
python
# continue 语句示例
for i in range(1, 10):
if i % 2 == 0:
continue # 跳过偶数
print(f"奇数: {i}")2.3.4 break
break 语句用于立即终止循环,跳出循环体。
python
# break 语句示例
for i in range(1, 10):
if i == 5:
break # 当 i 等于 5 时终止循环
print(f"数字: {i}")2.3.5 pass
pass 语句是一个空语句,用于占位,不执行任何操作。
python
# pass 语句示例
for i in range(5):
if i == 2:
pass # 占位,什么都不做
else:
print(f"数字: {i}")
# 定义空函数
def empty_function():
pass # 占位,等待后续实现
# 定义空类
class EmptyClass:
pass # 占位,等待后续实现第 3 章 容器数据类型
容器数据类型是用于存储多个元素的数据结构,Python 提供了多种容器类型,包括序列、列表、字符串、元组、集合和字典。
3.1 序列
序列是一种有序的容器类型,元素可以通过索引访问。Python 中的序列包括列表、字符串和元组。
序列的共同特点:
- 有序性:元素按照一定的顺序排列
- 可索引:可以通过索引访问元素
- 可切片:可以通过切片操作获取子序列
- 长度可变(除元组外):可以添加、删除或修改元素
python
# 序列索引示例
my_list = [1, 2, 3, 4, 5]
my_string = "Hello"
my_tuple = (1, 2, 3, 4, 5)
# 访问第一个元素(索引从0开始)
print(my_list[0]) # 输出: 1
print(my_string[0]) # 输出: H
print(my_tuple[0]) # 输出: 1
# 切片操作
print(my_list[1:3]) # 输出: [2, 3]
print(my_string[1:3]) # 输出: el
print(my_tuple[1:3]) # 输出: (2, 3)3.2 列表List
列表(List)是一种可变的序列类型,使用方括号 [] 定义,元素之间用逗号分隔。
3.2.1 创建列表
python
# 创建空列表
empty_list = []
# 创建包含元素的列表
numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "cherry"]
mixed = [1, "apple", 3.14, True]
# 使用 list() 函数创建列表
list_from_string = list("Hello") # 输出: ['H', 'e', 'l', 'l', 'o']
list_from_tuple = list((1, 2, 3)) # 输出: [1, 2, 3]
list_from_range = list(range(10)) # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]3.2.2 访问列表
python
fruits = ["apple", "banana", "cherry", "orange"]
# 通过索引访问元素(索引从0开始)
print(fruits[0]) # 输出: apple
print(fruits[2]) # 输出: cherry
# 负索引(从末尾开始)
print(fruits[-1]) # 输出: orange
print(fruits[-2]) # 输出: cherry
# 切片操作
print(fruits[1:3]) # 输出: ['banana', 'cherry']
print(fruits[:2]) # 输出: ['apple', 'banana']
print(fruits[2:]) # 输出: ['cherry', 'orange']
print(fruits[::2]) # 输出: ['apple', 'cherry']3.2.3 向列表中添加元素
python
fruits = ["apple", "banana"]
# append() 方法:在列表末尾添加元素
fruits.append("cherry")
print(fruits) # 输出: ['apple', 'banana', 'cherry']
# insert() 方法:在指定位置添加元素
fruits.insert(1, "orange")
print(fruits) # 输出: ['apple', 'orange', 'banana', 'cherry']
# extend() 方法:扩展列表,添加多个元素
fruits.extend(["grape", "kiwi"])
print(fruits) # 输出: ['apple', 'orange', 'banana', 'cherry', 'grape', 'kiwi']3.2.4 列表相加
python
list1 = [1, 2, 3]
list2 = [4, 5, 6]
# 使用 + 运算符
combined = list1 + list2
print(combined) # 输出: [1, 2, 3, 4, 5, 6]
# 使用 extend() 方法
list1.extend(list2)
print(list1) # 输出: [1, 2, 3, 4, 5, 6]3.2.5 列表乘法
python
# 使用 * 运算符重复列表
numbers = [1, 2, 3]
repeated = numbers * 3
print(repeated) # 输出: [1, 2, 3, 1, 2, 3, 1, 2, 3]
# 创建指定长度的空列表
empty = [None] * 5
print(empty) # 输出: [None, None, None, None, None]3.2.6 修改列表中元素
python
fruits = ["apple", "banana", "cherry"]
# 修改单个元素
fruits[1] = "orange"
print(fruits) # 输出: ['apple', 'orange', 'cherry']
# 修改多个元素(切片赋值)
fruits[1:] = ["grape", "kiwi"]
print(fruits) # 输出: ['apple', 'grape', 'kiwi']3.2.7 检查成员是否为列表中元素
python
fruits = ["apple", "banana", "cherry"]
# 使用 in 运算符
print("apple" in fruits) # 输出: True
print("orange" in fruits) # 输出: False
# 使用 not in 运算符
print("orange" not in fruits) # 输出: True3.2.8 获取列表长度
python
fruits = ["apple", "banana", "cherry"]
# 使用 len() 函数
length = len(fruits)
print(length) # 输出: 33.2.9 求列表中元素的最大值、最小值、加和
python
numbers = [10, 5, 8, 3, 12]
# 最大值
print(max(numbers)) # 输出: 12
# 最小值
print(min(numbers)) # 输出: 3
# 加和
print(sum(numbers)) # 输出: 38
# 平均值
average = sum(numbers) / len(numbers)
print(average) # 输出: 7.63.2.10 遍历列表
python
fruits = ["apple", "banana", "cherry"]
# 方法1:直接遍历元素
for fruit in fruits:
print(fruit)
# 方法2:使用索引
for i in range(len(fruits)):
print(f"索引 {i}: {fruits[i]}")
# 方法3:使用 enumerate() 函数
for index, fruit in enumerate(fruits):
print(f"索引 {index}: {fruit}")3.2.11 删除列表指定位置元素或者切片
python
fruits = ["apple", "banana", "cherry", "orange", "grape"]
# 删除指定位置的元素(使用 del)
del fruits[1]
print(fruits) # 输出: ['apple', 'cherry', 'orange', 'grape']
# 删除切片
del fruits[1:3]
print(fruits) # 输出: ['apple', 'grape']
# 使用 pop() 方法删除并返回指定位置的元素
popped = fruits.pop(0)
print(popped) # 输出: apple
print(fruits) # 输出: ['grape']
# 使用 remove() 方法删除指定值的元素
fruits = ["apple", "banana", "cherry"]
fruits.remove("banana")
print(fruits) # 输出: ['apple', 'cherry']3.2.12 嵌套列表
python
# 创建嵌套列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 访问嵌套列表的元素
print(matrix[0][1]) # 输出: 2
print(matrix[2][2]) # 输出: 9
# 修改嵌套列表的元素
matrix[1][1] = 10
print(matrix) # 输出: [[1, 2, 3], [4, 10, 6], [7, 8, 9]]
# 遍历嵌套列表
for row in matrix:
for element in row:
print(element, end=" ")
print()3.2.13 列表推导式
python
# 基本列表推导式
numbers = [1, 2, 3, 4, 5]
squared = [x ** 2 for x in numbers]
print(squared) # 输出: [1, 4, 9, 16, 25]
# 带条件的列表推导式
even_squared = [x ** 2 for x in numbers if x % 2 == 0]
print(even_squared) # 输出: [4, 16]
# 嵌套列表推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [element for row in matrix for element in row]
print(flattened) # 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 带条件的嵌套列表推导式
even_in_matrix = [element for row in matrix for element in row if element % 2 == 0]
print(even_in_matrix) # 输出: [2, 4, 6, 8]3.2.14 zip()函数
python
# 使用 zip() 函数组合多个列表
names = ["Alice", "Bob", "Charlie"]
ages = [18, 25, 30]
cities = ["New York", "London", "Paris"]
# 组合成元组的列表
combined = list(zip(names, ages, cities))
print(combined) # 输出: [('Alice', 18, 'New York'), ('Bob', 25, 'London'), ('Charlie', 30, 'Paris')]
# 遍历组合后的列表
for name, age, city in zip(names, ages, cities):
print(f"{name} is {age} years old and lives in {city}")
# 解压缩
zipped = list(zip(names, ages))
unzipped_names, unzipped_ages = zip(*zipped)
print(unzipped_names) # 输出: ('Alice', 'Bob', 'Charlie')
print(unzipped_ages) # 输出: (18, 25, 30)3.2.15 常用函数
python
# sort() 方法:排序(原地修改)
numbers = [5, 2, 8, 1, 3]
numbers.sort()
print(numbers) # 输出: [1, 2, 3, 5, 8]
# sorted() 函数:排序(返回新列表)
numbers = [5, 2, 8, 1, 3]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # 输出: [1, 2, 3, 5, 8]
print(numbers) # 输出: [5, 2, 8, 1, 3](原列表不变)
# reverse() 方法:反转(原地修改)
numbers = [1, 2, 3, 4, 5]
numbers.reverse()
print(numbers) # 输出: [5, 4, 3, 2, 1]
# reversed() 函数:反转(返回迭代器)
numbers = [1, 2, 3, 4, 5]
reversed_numbers = list(reversed(numbers))
print(reversed_numbers) # 输出: [5, 4, 3, 2, 1]
# count() 方法:统计元素出现次数
fruits = ["apple", "banana", "apple", "cherry", "apple"]
print(fruits.count("apple")) # 输出: 3
# index() 方法:查找元素第一次出现的索引
print(fruits.index("banana")) # 输出: 1
# clear() 方法:清空列表
fruits.clear()
print(fruits) # 输出: []
# copy() 方法:复制列表
original = [1, 2, 3]
copy = original.copy()
copy[0] = 100
print(original) # 输出: [1, 2, 3]
print(copy) # 输出: [100, 2, 3]3.3 字符串String
字符串(String)是一种不可变的序列类型,使用单引号、双引号或三引号定义。
3.3.1 创建字符串
python
# 使用单引号
single_quote = 'Hello'
# 使用双引号
double_quote = "World"
# 使用三引号(可以换行)
triple_quote = '''Hello
World''' # 多行字符串
# 使用 str() 函数创建字符串
str_from_int = str(123)
str_from_float = str(3.14)
str_from_bool = str(True)3.3.2 访问字符串
python
text = "Hello, World!"
# 通过索引访问字符(索引从0开始)
print(text[0]) # 输出: H
print(text[7]) # 输出: W
# 负索引(从末尾开始)
print(text[-1]) # 输出: !
print(text[-6]) # 输出: W
# 切片操作
print(text[0:5]) # 输出: Hello
print(text[7:]) # 输出: World!
print(text[:5]) # 输出: Hello
print(text[::2]) # 输出: Hlo ol!3.3.3 字符串相加
python
# 使用 + 运算符拼接字符串
greeting = "Hello" + " " + "World"
print(greeting) # 输出: Hello World
# 使用 += 运算符
message = "Hello"
message += " " + "World"
print(message) # 输出: Hello World
# 使用 join() 方法拼接多个字符串
words = ["Hello", "World", "!"]
greeting = " ".join(words)
print(greeting) # 输出: Hello World !3.3.4 字符串乘法
python
# 使用 * 运算符重复字符串
repeated = "Hello" * 3
print(repeated) # 输出: HelloHelloHello
# 创建指定长度的分隔线
dash_line = "-" * 20
print(dash_line) # 输出: --------------------
# 创建重复的模式
pattern = "ab" * 5
print(pattern) # 输出: ababababab3.3.5 检查成员是否为字符串中元素
python
text = "Hello, World!"
# 使用 in 运算符
print("Hello" in text) # 输出: True
print("Python" in text) # 输出: False
# 使用 not in 运算符
print("Python" not in text) # 输出: True
# 检查单个字符
print("H" in text) # 输出: True
print("h" in text) # 输出: False(区分大小写)3.3.6 原始字符串
原始字符串(Raw String)使用前缀 r 或 R,可以避免转义字符的特殊处理。
python
# 普通字符串中的转义字符
path = "C:\\Users\\name\\Documents"
print(path) # 输出: C:\Users\name\Documents
# 原始字符串
raw_path = r"C:\Users\name\Documents"
print(raw_path) # 输出: C:\Users\name\Documents
# 原始字符串中的引号
raw_string = r"It's a raw string"
print(raw_string) # 输出: It's a raw string
# 原始字符串不能以反斜杠结尾
# raw_string = r"This is a raw string with backslash at the end\" # 会引发语法错误3.3.7 常用函数
python
text = " Hello, World! "
# len() 函数:获取字符串长度
print(len(text)) # 输出: 15
# str.lower():转换为小写
print(text.lower()) # 输出: hello, world!
# str.upper():转换为大写
print(text.upper()) # 输出: HELLO, WORLD!
# str.strip():去除首尾空白
print(text.strip()) # 输出: Hello, World!
# str.lstrip():去除左侧空白
print(text.lstrip()) # 输出: Hello, World!
# str.rstrip():去除右侧空白
print(text.rstrip()) # 输出: Hello, World!
# str.split():分割字符串
text = "Hello, World!"
print(text.split(",")) # 输出: ['Hello', ' World!']
print(text.split()) # 输出: ['Hello,', 'World!']
# str.join():拼接字符串
words = ["Hello", "World", "!"]
print(" ".join(words)) # 输出: Hello World !
# str.replace():替换子字符串
text = "Hello, World!"
print(text.replace("World", "Python")) # 输出: Hello, Python!
# str.find():查找子字符串位置
text = "Hello, World!"
print(text.find("World")) # 输出: 7
print(text.find("Python")) # 输出: -1(未找到)
# str.index():查找子字符串位置(未找到会引发错误)
print(text.index("World")) # 输出: 7
# print(text.index("Python")) # 会引发 ValueError3.3.8 其他函数
python
text = "Hello, World!"
# str.startswith():检查是否以指定字符串开头
print(text.startswith("Hello")) # 输出: True
print(text.startswith("World")) # 输出: False
# str.endswith():检查是否以指定字符串结尾
print(text.endswith("!")) # 输出: True
print(text.endswith("World")) # 输出: False
# str.isalpha():检查是否全为字母
print("Hello".isalpha()) # 输出: True
print("Hello123".isalpha()) # 输出: False
# str.isdigit():检查是否全为数字
print("123".isdigit()) # 输出: True
print("123a".isdigit()) # 输出: False
# str.isalnum():检查是否全为字母和数字
print("Hello123".isalnum()) # 输出: True
print("Hello 123".isalnum()) # 输出: False
# str.islower():检查是否全为小写
print("hello".islower()) # 输出: True
print("Hello".islower()) # 输出: False
# str.isupper():检查是否全为大写
print("HELLO".isupper()) # 输出: True
print("Hello".isupper()) # 输出: False
# str.capitalize():首字母大写
print("hello world".capitalize()) # 输出: Hello world
# str.title():每个单词首字母大写
print("hello world".title()) # 输出: Hello World
# str.center():居中对齐
print("Hello".center(10, "*")) # 输出: **Hello***
# str.ljust():左对齐
print("Hello".ljust(10, "*")) # 输出: Hello*****
# str.rjust():右对齐
print("Hello".rjust(10, "*")) # 输出: *****Hello
# str.zfill():填充零
print("42".zfill(5)) # 输出: 00042
print("-42".zfill(5)) # 输出: -00423.4 元组Tuple
元组(Tuple)是一种不可变的序列类型,使用圆括号 () 定义,元素之间用逗号分隔。
3.4.1 创建元组
python
# 创建空元组
empty_tuple = ()
# 创建包含元素的元组
numbers = (1, 2, 3, 4, 5)
fruits = ("apple", "banana", "cherry")
# 单元素元组(需要加逗号)
single_element = (42,)
# 不使用括号创建元组(仅适用于简单情况)
another_tuple = 1, 2, 3
print(type(another_tuple)) # 输出: <class 'tuple'>
# 使用 tuple() 函数创建元组
tuple_from_list = tuple([1, 2, 3]) # 输出: (1, 2, 3)
tuple_from_string = tuple("Hello") # 输出: ('H', 'e', 'l', 'l', 'o')
tuple_from_range = tuple(range(5)) # 输出: (0, 1, 2, 3, 4)3.4.2 访问元组
python
fruits = ("apple", "banana", "cherry", "orange")
# 通过索引访问元素(索引从0开始)
print(fruits[0]) # 输出: apple
print(fruits[2]) # 输出: cherry
# 负索引(从末尾开始)
print(fruits[-1]) # 输出: orange
print(fruits[-2]) # 输出: cherry
# 切片操作
print(fruits[1:3]) # 输出: ('banana', 'cherry')
print(fruits[:2]) # 输出: ('apple', 'banana')
print(fruits[2:]) # 输出: ('cherry', 'orange')
print(fruits[::2]) # 输出: ('apple', 'cherry')3.4.3 元组相加
python
# 使用 + 运算符拼接元组
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined = tuple1 + tuple2
print(combined) # 输出: (1, 2, 3, 4, 5, 6)
# 使用 += 运算符
tuple1 += tuple2
print(tuple1) # 输出: (1, 2, 3, 4, 5, 6)3.4.4 元组乘法
python
# 使用 * 运算符重复元组
numbers = (1, 2, 3)
repeated = numbers * 3
print(repeated) # 输出: (1, 2, 3, 1, 2, 3, 1, 2, 3)
# 创建指定长度的元组
empty = (None,) * 5
print(empty) # 输出: (None, None, None, None, None)3.4.5 检查成员是否为元组中元素
python
fruits = ("apple", "banana", "cherry")
# 使用 in 运算符
print("apple" in fruits) # 输出: True
print("orange" in fruits) # 输出: False
# 使用 not in 运算符
print("orange" not in fruits) # 输出: True
# 检查单个元素
print("banana" in fruits) # 输出: True3.4.6 获取元组长度
python
fruits = ("apple", "banana", "cherry")
# 使用 len() 函数
length = len(fruits)
print(length) # 输出: 33.4.7 求元组中元素的最大值、最小值、加和
python
numbers = (10, 5, 8, 3, 12)
# 最大值
print(max(numbers)) # 输出: 12
# 最小值
print(min(numbers)) # 输出: 3
# 加和
print(sum(numbers)) # 输出: 38
# 平均值
average = sum(numbers) / len(numbers)
print(average) # 输出: 7.63.4.8 遍历元组
python
fruits = ("apple", "banana", "cherry")
# 方法1:直接遍历元素
for fruit in fruits:
print(fruit)
# 方法2:使用索引
for i in range(len(fruits)):
print(f"索引 {i}: {fruits[i]}")
# 方法3:使用 enumerate() 函数
for index, fruit in enumerate(fruits):
print(f"索引 {index}: {fruit}")3.4.9 元组的不可变性
元组是不可变的,这意味着一旦创建,就不能修改其元素。
python
# 元组是不可变的
my_tuple = (1, 2, 3)
# my_tuple[0] = 4 # 会引发 TypeError: 'tuple' object does not support item assignment
# 但元组可以包含可变对象
mixed_tuple = (1, "apple", [2, 3, 4])
print(mixed_tuple) # 输出: (1, 'apple', [2, 3, 4])
# 可以修改元组中的可变对象
mixed_tuple[2].append(5)
print(mixed_tuple) # 输出: (1, 'apple', [2, 3, 4, 5])
# 元组的其他特点
# 元组可以包含不同类型的元素
mixed_tuple = (1, "apple", 3.14, True)
# 元组可以嵌套
tuple_of_tuples = ((1, 2), (3, 4), (5, 6))
# 元组可以用于多个变量赋值
x, y, z = (1, 2, 3)
print(x, y, z) # 输出: 1 2 3
# 元组可以用于函数返回多个值
def get_coordinates():
return (10, 20, 30)
x, y, z = get_coordinates()
print(x, y, z) # 输出: 10 20 303.5 集合Set
集合(Set)是一种无序、无重复元素的容器类型,使用大括号 {} 或 set() 函数定义。
3.5.1 创建集合
python
# 创建空集合(必须使用 set())
empty_set = set()
# 创建包含元素的集合
numbers = {1, 2, 3, 4, 5}
fruits = {"apple", "banana", "cherry"}
# 使用 set() 函数创建集合
set_from_list = set([1, 2, 2, 3, 3, 3]) # 自动去重,输出: {1, 2, 3}
set_from_string = set("Hello") # 输出: {'H', 'e', 'l', 'o'}
set_from_range = set(range(5)) # 输出: {0, 1, 2, 3, 4}
# 创建集合的其他方式
set_from_tuple = set((1, 2, 3, 2)) # 输出: {1, 2, 3}3.5.2 向集合中添加元素
python
# 添加单个元素
fruits = {"apple", "banana"}
fruits.add("cherry")
print(fruits) # 输出: {'apple', 'banana', 'cherry'}
# 添加多个元素(使用 update() 方法)
fruits.update(["orange", "grape"])
print(fruits) # 输出: {'apple', 'banana', 'cherry', 'orange', 'grape'}
# 使用其他可迭代对象更新集合
fruits.update({"kiwi", "mango"})
print(fruits) # 输出: {'apple', 'banana', 'cherry', 'orange', 'grape', 'kiwi', 'mango'}3.5.3 从集合中删除元素
python
fruits = {"apple", "banana", "cherry", "orange", "grape"}
# remove() 方法:删除指定元素,如果元素不存在会引发错误
fruits.remove("banana")
print(fruits) # 输出: {'apple', 'cherry', 'orange', 'grape'}
# discard() 方法:删除指定元素,如果元素不存在不会引发错误
fruits.discard("orange")
print(fruits) # 输出: {'apple', 'cherry', 'grape'}
# pop() 方法:删除并返回任意元素
popped = fruits.pop()
print(popped) # 输出: 任意元素,如 'apple'
print(fruits) # 输出: 剩余元素,如 {'cherry', 'grape'}
# clear() 方法:清空集合
fruits.clear()
print(fruits) # 输出: set()3.5.4 检查成员是否为集合中元素
python
fruits = {"apple", "banana", "cherry"}
# 使用 in 运算符
print("apple" in fruits) # 输出: True
print("orange" in fruits) # 输出: False
# 使用 not in 运算符
print("orange" not in fruits) # 输出: True3.5.5 获取集合长度
python
fruits = {"apple", "banana", "cherry"}
# 使用 len() 函数
length = len(fruits)
print(length) # 输出: 33.5.6 求集合中元素的最大值、最小值、加和
python
numbers = {10, 5, 8, 3, 12}
# 最大值
print(max(numbers)) # 输出: 12
# 最小值
print(min(numbers)) # 输出: 3
# 加和
print(sum(numbers)) # 输出: 38
# 平均值
average = sum(numbers) / len(numbers)
print(average) # 输出: 7.63.5.7 遍历集合
python
fruits = {"apple", "banana", "cherry"}
# 方法1:直接遍历元素
for fruit in fruits:
print(fruit)
# 方法2:使用 sorted() 函数按顺序遍历
sorted_fruits = sorted(fruits)
for fruit in sorted_fruits:
print(fruit)
# 方法3:使用 enumerate() 函数
for index, fruit in enumerate(fruits):
print(f"索引 {index}: {fruit}")3.5.8 常用函数
python
# 集合运算
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
# 并集:所有元素
print(a.union(b)) # 输出: {1, 2, 3, 4, 5, 6}
print(a | b) # 输出: {1, 2, 3, 4, 5, 6}
# 交集:共同元素
print(a.intersection(b)) # 输出: {3, 4}
print(a & b) # 输出: {3, 4}
# 差集:a 中有但 b 中没有的元素
print(a.difference(b)) # 输出: {1, 2}
print(a - b) # 输出: {1, 2}
# 对称差集:a 和 b 中不同时存在的元素
print(a.symmetric_difference(b)) # 输出: {1, 2, 5, 6}
print(a ^ b) # 输出: {1, 2, 5, 6}
# 子集判断:a 是否是 b 的子集
c = {1, 2}
print(c.issubset(a)) # 输出: True
print(c.issubset(b)) # 输出: False
# 超集判断:a 是否是 c 的超集
print(a.issuperset(c)) # 输出: True
# 不相交判断:两个集合是否没有共同元素
d = {7, 8, 9}
print(a.isdisjoint(d)) # 输出: True
print(a.isdisjoint(b)) # 输出: False
# 集合推导式
squared = {x ** 2 for x in range(1, 6)}
print(squared) # 输出: {1, 4, 9, 16, 25}
# 带条件的集合推导式
even_squared = {x ** 2 for x in range(1, 10) if x % 2 == 0}
print(even_squared) # 输出: {16, 4, 36, 64}3.6 字典Dictionary
字典(Dictionary)是一种键值对的无序容器类型,使用大括号 {} 定义,键值对之间用逗号分隔,键和值之间用冒号 : 分隔。
3.6.1 创建字典
python
# 创建空字典
empty_dict = {}
# 创建包含键值对的字典
person = {"name": "Alice", "age": 18, "city": "New York"}
# 使用 dict() 函数创建字典
dict_from_list = dict([("name", "Bob"), ("age", 25)]) # 输出: {'name': 'Bob', 'age': 25}
dict_from_kwargs = dict(name="Charlie", age=30) # 输出: {'name': 'Charlie', 'age': 30}
# 使用字典推导式创建字典
squares = {x: x**2 for x in range(1, 6)}
print(squares) # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 从两个列表创建字典
keys = ["a", "b", "c"]
values = [1, 2, 3]
dict_from_lists = dict(zip(keys, values))
print(dict_from_lists) # 输出: {'a': 1, 'b': 2, 'c': 3}3.6.2 访问字典
python
person = {"name": "Alice", "age": 18, "city": "New York"}
# 方法1:使用方括号访问(键不存在会引发错误)
print(person["name"]) # 输出: Alice
# print(person["country"]) # 会引发 KeyError: 'country'
# 方法2:使用 get() 方法(键不存在返回 None)
print(person.get("age")) # 输出: 18
print(person.get("country")) # 输出: None
# 方法3:使用 get() 方法并指定默认值
print(person.get("country", "Unknown")) # 输出: Unknown
# 方法4:使用 setdefault() 方法(键不存在时设置默认值并返回)
print(person.setdefault("country", "USA")) # 输出: USA
print(person) # 输出: {'name': 'Alice', 'age': 18, 'city': 'New York', 'country': 'USA'}3.6.3 向字典中添加元素
python
person = {"name": "Alice", "age": 18}
# 方法1:直接赋值(键不存在时添加)
person["city"] = "New York"
print(person) # 输出: {'name': 'Alice', 'age': 18, 'city': 'New York'}
# 方法2:使用 update() 方法添加多个键值对
person.update({"country": "USA", "email": "alice@example.com"})
print(person) # 输出: {'name': 'Alice', 'age': 18, 'city': 'New York', 'country': 'USA', 'email': 'alice@example.com'}
# 使用关键字参数添加
person.update(phone="123-456-7890")
print(person) # 输出: {'name': 'Alice', 'age': 18, 'city': 'New York', 'country': 'USA', 'email': 'alice@example.com', 'phone': '123-456-7890'}3.6.4 修改字典中元素
python
person = {"name": "Alice", "age": 18, "city": "New York"}
# 方法1:直接赋值(键存在时修改)
person["age"] = 19
print(person) # 输出: {'name': 'Alice', 'age': 19, 'city': 'New York'}
# 方法2:使用 update() 方法修改多个键值对
person.update({"age": 20, "city": "Boston"})
print(person) # 输出: {'name': 'Alice', 'age': 20, 'city': 'Boston'}3.6.5 检查成员是否为字典中的key
python
person = {"name": "Alice", "age": 18, "city": "New York"}
# 使用 in 运算符检查键是否存在
print("name" in person) # 输出: True
print("country" in person) # 输出: False
# 使用 not in 运算符检查键是否不存在
print("country" not in person) # 输出: True
# 注意:in 运算符只检查键,不检查值
print("Alice" in person) # 输出: False("Alice" 是值,不是键)3.6.6 获取字典长度
python
person = {"name": "Alice", "age": 18, "city": "New York"}
# 使用 len() 函数
length = len(person)
print(length) # 输出: 33.6.7 遍历字典
python
person = {"name": "Alice", "age": 18, "city": "New York"}
# 方法1:遍历键
for key in person:
print(key, ":", person[key])
# 方法2:使用 keys() 方法遍历键
for key in person.keys():
print(key, ":", person[key])
# 方法3:使用 values() 方法遍历值
for value in person.values():
print(value)
# 方法4:使用 items() 方法遍历键值对
for key, value in person.items():
print(key, ":", value)
# 方法5:使用 sorted() 函数按键排序遍历
for key in sorted(person):
print(key, ":", person[key])3.6.8 删除字典元素
python
person = {"name": "Alice", "age": 18, "city": "New York", "country": "USA"}
# 方法1:使用 del 语句
print("删除前:", person)
del person["age"]
print("删除后:", person) # 输出: {'name': 'Alice', 'city': 'New York', 'country': 'USA'}
# 方法2:使用 pop() 方法(删除并返回值)
city = person.pop("city")
print("删除的城市:", city) # 输出: New York
print("删除后:", person) # 输出: {'name': 'Alice', 'country': 'USA'}
# pop() 方法可以指定默认值
email = person.pop("email", "Not found")
print("邮箱:", email) # 输出: Not found
# 方法3:使用 popitem() 方法(删除并返回最后一个键值对)
item = person.popitem()
print("删除的项:", item) # 输出: ('country', 'USA')
print("删除后:", person) # 输出: {'name': 'Alice'}
# 方法4:使用 clear() 方法清空字典
person.clear()
print("清空后:", person) # 输出: {}3.6.9 常用函数
python
# 字典操作
dict1 = {"a": 1, "b": 2, "c": 3}
dict2 = {"c": 4, "d": 5, "e": 6}
# 合并字典(Python 3.9+)
merged = dict1 | dict2
print(merged) # 输出: {'a': 1, 'b': 2, 'c': 4, 'd': 5, 'e': 6}
# 更新字典
dict1 |= dict2
print(dict1) # 输出: {'a': 1, 'b': 2, 'c': 4, 'd': 5, 'e': 6}
# 字典方法
d = {"name": "Alice", "age": 18, "city": "New York"}
# keys():返回所有键
print(d.keys()) # 输出: dict_keys(['name', 'age', 'city'])
# values():返回所有值
print(d.values()) # 输出: dict_values(['Alice', 18, 'New York'])
# items():返回所有键值对
print(d.items()) # 输出: dict_items([('name', 'Alice'), ('age', 18), ('city', 'New York')])
# copy():复制字典
new_dict = d.copy()
print(new_dict) # 输出: {'name': 'Alice', 'age': 18, 'city': 'New York'}
# fromkeys():从序列创建字典
keys = ["name", "age", "city"]
default_value = "Unknown"
new_dict = dict.fromkeys(keys, default_value)
print(new_dict) # 输出: {'name': 'Unknown', 'age': 'Unknown', 'city': 'Unknown'}
# 字典推导式
# 创建平方字典
squares = {x: x**2 for x in range(1, 6)}
print(squares) # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 带条件的字典推导式
even_squares = {x: x**2 for x in range(1, 10) if x % 2 == 0}
print(even_squares) # 输出: {2: 4, 4: 16, 6: 36, 8: 64}
# 嵌套字典
nested_dict = {
"person1": {"name": "Alice", "age": 18},
"person2": {"name": "Bob", "age": 25}
}
print(nested_dict["person1"]["name"]) # 输出: Alice3.7 列表、元组、字典和集合的区别
| 特性 | 列表 (List) | 元组 (Tuple) | 字典 (Dictionary) | 集合 (Set) |
|---|---|---|---|---|
| 定义方式 | [] | () | {} | {} 或 set() |
| 可变性 | 可变 | 不可变 | 可变 | 可变 |
| 元素类型 | 任意类型 | 任意类型 | 键:不可变类型 值:任意类型 | 不可变类型 |
| 元素唯一性 | 允许重复 | 允许重复 | 键唯一,值可重复 | 元素唯一 |
| 索引方式 | 数字索引 | 数字索引 | 键索引 | 无索引 |
| 有序性 | 有序 | 有序 | 无序(Python 3.7+ 插入有序) | 无序 |
| 主要用途 | 存储有序可变数据 | 存储有序不可变数据 | 存储键值对数据 | 存储唯一元素集合 |
python
# 示例:不同容器类型的使用场景
# 列表:存储需要修改的有序数据
shopping_list = ["apple", "banana", "milk"]
shopping_list.append("bread")
# 元组:存储不需要修改的有序数据
coordinates = (10.0, 20.0)
# 字典:存储键值对数据
student = {"name": "Alice", "grade": 95, "major": "Computer Science"}
# 集合:存储唯一元素
unique_numbers = {1, 2, 3, 4, 5}
duplicate_numbers = [1, 2, 2, 3, 3, 3]
unique_from_duplicates = set(duplicate_numbers) # 去重
print(unique_from_duplicates) # 输出: {1, 2, 3}第 4 章 函数
函数是一段具有特定功能的、可重用的代码块。函数可以接收输入参数,执行特定的操作,并返回结果。
4.1 函数的概念
函数是一种封装代码的方式,它将一段逻辑组织成一个独立的单元,使得代码更加模块化、可维护和可重用。
4.2 函数的定义
在 Python 中,使用 def 关键字来定义函数,语法如下:
python
def 函数名(参数1, 参数2, ...):
"""函数文档字符串"""
# 函数体
# 执行操作
return 返回值示例:
python
# 定义一个简单的函数
def greet():
"""打印问候信息"""
print("Hello, welcome to Python!")
# 定义一个带参数的函数
def greet_name(name):
"""向指定名称的人打招呼"""
print(f"Hello, {name}!")
# 定义一个带返回值的函数
def add(a, b):
"""计算两个数的和"""
return a + b4.3 函数的抽取以及调用
函数的抽取:将重复的代码逻辑抽取到一个函数中,以便多次使用。
函数的调用:通过函数名和传递的参数来执行函数。
示例:
python
# 抽取函数
def calculate_area(length, width):
"""计算矩形面积"""
return length * width
# 调用函数
area1 = calculate_area(5, 3)
print(f"面积1: {area1}") # 输出: 面积1: 15
area2 = calculate_area(10, 2)
print(f"面积2: {area2}") # 输出: 面积2: 204.4 使用函数的好处
- 代码重用:避免重复编写相同的代码
- 模块化:将复杂的问题分解为更小的、可管理的部分
- 可维护性:更容易理解和修改代码
- 可读性:函数名可以清晰地表达代码的功能
- 可测试性:更容易对代码进行测试
4.5 函数的参数
Python 支持多种类型的函数参数:
4.5.1 位置参数
位置参数是最常见的参数类型,按照参数的位置顺序传递值。
python
def greet(name, age):
"""向指定名称和年龄的人打招呼"""
print(f"Hello, {name}! You are {age} years old.")
# 调用函数,按照位置传递参数
greet("Alice", 18) # 输出: Hello, Alice! You are 18 years old.4.5.2 关键字参数
关键字参数使用参数名来指定值,不依赖于位置。
python
def greet(name, age):
"""向指定名称和年龄的人打招呼"""
print(f"Hello, {name}! You are {age} years old.")
# 使用关键字参数调用函数
greet(age=18, name="Alice") # 输出: Hello, Alice! You are 18 years old.4.5.3 默认参数
默认参数为参数提供默认值,当调用函数时不提供该参数的值时,使用默认值。
python
def greet(name, age=18):
"""向指定名称的人打招呼,默认年龄为18"""
print(f"Hello, {name}! You are {age} years old.")
# 调用函数时不提供年龄参数
greet("Alice") # 输出: Hello, Alice! You are 18 years old.
# 调用函数时提供年龄参数
greet("Bob", 25) # 输出: Hello, Bob! You are 25 years old.4.5.4 可变位置参数
可变位置参数允许函数接收任意数量的位置参数,使用 * 前缀。
python
def sum_numbers(*args):
"""计算任意数量数字的和"""
total = 0
for num in args:
total += num
return total
# 调用函数,传递任意数量的参数
print(sum_numbers(1, 2, 3)) # 输出: 6
print(sum_numbers(1, 2, 3, 4, 5)) # 输出: 154.5.5 可变关键字参数
可变关键字参数允许函数接收任意数量的关键字参数,使用 ** 前缀。
python
def print_info(**kwargs):
"""打印任意数量的关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
# 调用函数,传递任意数量的关键字参数
print_info(name="Alice", age=18, city="New York")
# 输出:
# name: Alice
# age: 18
# city: New York4.5.6 混合使用不同类型的参数
python
def mixed_params(a, b, *args, c=10, **kwargs):
"""混合使用不同类型的参数"""
print(f"a: {a}, b: {b}")
print(f"args: {args}")
print(f"c: {c}")
print(f"kwargs: {kwargs}")
# 调用函数
mixed_params(1, 2, 3, 4, 5, c=20, d=30, e=40)
# 输出:
# a: 1, b: 2
# args: (3, 4, 5)
# c: 20
# kwargs: {'d': 30, 'e': 40}4.5.7 解包传参
解包传参是指将可迭代对象或字典的元素解包后传递给函数。
示例:
python
# 列表解包传参
def add(a, b, c):
"""计算三个数的和"""
return a + b + c
numbers = [1, 2, 3]
result = add(*numbers) # 解包列表
print(result) # 输出: 6
# 元组解包传参
coordinates = (10, 20, 30)
def print_coordinates(x, y, z):
"""打印坐标"""
print(f"x: {x}, y: {y}, z: {z}")
print_coordinates(*coordinates) # 解包元组
# 输出: x: 10, y: 20, z: 30
# 字典解包传参
def person_info(name, age, city):
"""打印个人信息"""
print(f"Name: {name}, Age: {age}, City: {city}")
person = {"name": "Alice", "age": 18, "city": "New York"}
person_info(**person) # 解包字典
# 输出: Name: Alice, Age: 18, City: New York4.5.8 强制使用位置参数或关键字参数
Python 3.8+ 支持使用 / 和 * 来强制指定参数的传递方式:
/前的参数必须使用位置传参*后的参数必须使用关键字传参
示例:
python
def f(a, b, /, c, d, *, e, f):
"""强制使用位置参数或关键字参数"""
print(a, b, c, d, e, f)
# 正确的调用方式
f(1, 2, 3, d=4, e=5, f=6)
# 输出: 1 2 3 4 5 6
# 错误的调用方式
# f(a=1, b=2, 3, 4, e=5, f=6) # 错误:/ 前的参数不能使用关键字传参
# f(1, 2, 3, 4, 5, 6) # 错误:* 后的参数必须使用关键字传参4.6 函数说明文档
函数说明文档(也称为文档字符串)是函数定义中的字符串,用于描述函数的功能、参数和返回值。
示例:
python
def calculate_area(length, width):
"""计算矩形面积
参数:
length (float): 矩形的长度
width (float): 矩形的宽度
返回:
float: 矩形的面积
"""
return length * width
# 查看函数文档
help(calculate_area)
# 输出:
# Help on function calculate_area in module __main__:
#
# calculate_area(length, width)
# 计算矩形面积
#
# 参数:
# length (float): 矩形的长度
# width (float): 矩形的宽度
#
# 返回:
# float: 矩形的面积4.7 返回值
函数可以使用 return 语句返回一个值,也可以不返回任何值(默认返回 None)。
示例:
python
# 返回单个值
def add(a, b):
"""计算两个数的和"""
return a + b
result = add(3, 5)
print(result) # 输出: 8
# 返回多个值(使用元组)
def get_coordinates():
"""返回坐标"""
return 10, 20, 30
x, y, z = get_coordinates()
print(x, y, z) # 输出: 10 20 30
# 无返回值
def print_message():
"""打印消息"""
print("Hello, World!")
result = print_message()
print(result) # 输出: None4.8 函数嵌套调用
函数可以在一个函数内部调用另一个函数,这称为函数嵌套调用。
示例:
python
def add(a, b):
"""计算两个数的和"""
return a + b
def multiply(a, b):
"""计算两个数的积"""
return a * b
def calculate(a, b, c):
"""计算 (a + b) * c"""
sum_result = add(a, b)
return multiply(sum_result, c)
result = calculate(2, 3, 4)
print(result) # 输出: (2 + 3) * 4 = 204.9 变量的作用域
变量的作用域是指变量在程序中可以被访问的范围。Python 中有四种作用域:
- 局部作用域:在函数内部定义的变量,只能在函数内部访问
- 嵌套作用域:在嵌套函数中,内层函数可以访问外层函数的变量
- 全局作用域:在模块级别定义的变量,可以在模块的任何地方访问
- 内置作用域:Python 内置的变量和函数,如
print(),len()等
示例:
python
# 全局变量
global_var = "I'm global"
def outer_function():
# 嵌套作用域变量
outer_var = "I'm outer"
def inner_function():
# 局部变量
local_var = "I'm local"
print(local_var) # 可以访问局部变量
print(outer_var) # 可以访问嵌套作用域变量
print(global_var) # 可以访问全局变量
inner_function()
print(outer_var) # 可以访问嵌套作用域变量
print(global_var) # 可以访问全局变量
# print(local_var) # 无法访问局部变量,会引发错误
outer_function()
print(global_var) # 可以访问全局变量
# print(outer_var) # 无法访问嵌套作用域变量,会引发错误
# print(local_var) # 无法访问局部变量,会引发错误
# 使用 global 关键字修改全局变量
def modify_global():
global global_var
global_var = "Modified global"
print(global_var) # 输出: Modified global
modify_global()
print(global_var) # 输出: Modified global4.10 递归
递归是指函数调用自身的过程。递归函数通常有一个基线条件(停止条件)和一个递归条件(调用自身的条件)。
示例:
python
# 计算阶乘
def factorial(n):
"""计算 n 的阶乘"""
# 基线条件
if n == 0 or n == 1:
return 1
# 递归条件
else:
return n * factorial(n - 1)
print(factorial(5)) # 输出: 120 (5! = 5 * 4 * 3 * 2 * 1)
# 计算斐波那契数列
def fibonacci(n):
"""计算第 n 个斐波那契数"""
# 基线条件
if n <= 1:
return n
# 递归条件
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10)) # 输出: 554.11 匿名函数
匿名函数(也称为 lambda 函数)是一种没有名称的小型函数,使用 lambda 关键字定义。
语法:
python
lambda 参数1, 参数2, ...: 表达式示例:
python
# 创建一个简单的匿名函数
add = lambda x, y: x + y
print(add(3, 5)) # 输出: 8
# 在排序中使用匿名函数
students = [
{"name": "Alice", "grade": 95},
{"name": "Bob", "grade": 85},
{"name": "Charlie", "grade": 90}
]
# 按成绩排序
students.sort(key=lambda student: student["grade"])
print(students)
# 输出: [{'name': 'Bob', 'grade': 85}, {'name': 'Charlie', 'grade': 90}, {'name': 'Alice', 'grade': 95}]
# 按成绩降序排序
students.sort(key=lambda student: student["grade"], reverse=True)
print(students)
# 输出: [{'name': 'Alice', 'grade': 95}, {'name': 'Charlie', 'grade': 90}, {'name': 'Bob', 'grade': 85}]
# 在 filter() 函数中使用匿名函数
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # 输出: [2, 4, 6, 8, 10]
# 在 map() 函数中使用匿名函数
squared_numbers = list(map(lambda x: x ** 2, numbers))
print(squared_numbers) # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]第 5 章 文件操作
文件操作是编程中常见的任务,包括文件的创建、读取、写入、修改和删除等操作。Python 提供了丰富的文件操作功能,使得处理文件变得简单高效。
5.1 文件的基本概念
文件是存储在磁盘上的一组相关数据的集合,通常具有特定的扩展名来表示文件类型。
5.1.1 文件的分类
根据文件的存储方式和编码方式,文件可以分为:
- 文本文件:以文本形式存储,包含字符和换行符,如
.txt、.py、.md等 - 二进制文件:以二进制形式存储,包含字节数据,如
.jpg、.png、.exe等
5.1.2 文件的路径
文件路径是指文件在文件系统中的位置,分为:
- 绝对路径:从根目录开始的完整路径,如
C:\Users\name\Documents\file.txt(Windows)或/home/name/Documents/file.txt(Linux) - 相对路径:相对于当前工作目录的路径,如
./file.txt(当前目录)或../file.txt(上级目录)
示例:
python
import os
# 获取当前工作目录
print(os.getcwd()) # 输出当前工作目录
# 切换工作目录
# os.chdir("path/to/directory")
# 拼接路径
file_path = os.path.join("directory", "file.txt")
print(file_path) # 输出: directory\file.txt (Windows) 或 directory/file.txt (Linux)
# 检查路径是否存在
print(os.path.exists(file_path)) # 输出: True 或 False
# 检查是否为文件
print(os.path.isfile(file_path)) # 输出: True 或 False
# 检查是否为目录
print(os.path.isdir(file_path)) # 输出: True 或 False5.2 文件的打开与关闭
在 Python 中,使用 open() 函数打开文件,使用 close() 方法关闭文件。
语法:
python
file = open(file_path, mode)
# 操作文件
file.close()文件打开模式:
| 模式 | 描述 |
|---|---|
'r' | 只读模式(默认),文件不存在则报错 |
'w' | 写入模式,文件不存在则创建,存在则覆盖 |
'a' | 追加模式,文件不存在则创建,存在则在末尾追加 |
'x' | 独占创建模式,文件存在则报错 |
'b' | 二进制模式 |
't' | 文本模式(默认) |
'+' | 读写模式 |
示例:
python
# 使用 open() 和 close()
file = open("example.txt", "w")
try:
file.write("Hello, World!")
finally:
file.close() # 确保文件关闭
# 使用 with 语句(推荐)
with open("example.txt", "r") as file:
content = file.read()
print(content) # 输出: Hello, World!
# with 语句会自动关闭文件5.3 文件读写
5.3.1 写数据
示例:
python
# 写入文本文件
with open("example.txt", "w", encoding="utf-8") as file:
file.write("Hello, World!\n")
file.write("Python is great!\n")
# 写入多行
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("example.txt", "a", encoding="utf-8") as file:
file.writelines(lines)
# 写入二进制文件
with open("example.bin", "wb") as file:
file.write(b"Hello, Binary World!")5.3.2 读数据
示例:
python
# 读取整个文件
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
# 逐行读取
with open("example.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.strip()) # strip() 去除换行符和空格
# 读取指定字节数
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read(10) # 读取前10个字符
print(content)
# 读取二进制文件
with open("example.bin", "rb") as file:
content = file.read()
print(content) # 输出: b'Hello, Binary World!'5.4 常用函数
文件操作常用函数:
python
import os
import shutil
# 创建目录
os.makedirs("new_directory", exist_ok=True)
# 删除文件
if os.path.exists("example.txt"):
os.remove("example.txt")
# 删除目录
if os.path.exists("new_directory"):
os.rmdir("new_directory") # 只能删除空目录
# shutil.rmtree("new_directory") # 删除非空目录
# 重命名文件或目录
if os.path.exists("example.txt"):
os.rename("example.txt", "new_example.txt")
# 复制文件
if os.path.exists("new_example.txt"):
shutil.copy("new_example.txt", "copy_example.txt")
# 复制目录
if os.path.exists("source_directory"):
shutil.copytree("source_directory", "destination_directory")
# 获取文件大小
if os.path.exists("example.txt"):
size = os.path.getsize("example.txt")
print(f"文件大小: {size} 字节")
# 获取文件修改时间
if os.path.exists("example.txt"):
mtime = os.path.getmtime("example.txt")
print(f"修改时间: {mtime}")
# 转换为可读格式
import datetime
print(f"修改时间: {datetime.datetime.fromtimestamp(mtime)}")5.5 文件拷贝
示例:
python
def copy_file(source_path, destination_path):
"""拷贝文件
参数:
source_path (str): 源文件路径
destination_path (str): 目标文件路径
返回:
bool: 拷贝是否成功
"""
try:
# 打开源文件和目标文件
with open(source_path, "rb") as source_file:
with open(destination_path, "wb") as dest_file:
# 分块读取和写入
chunk_size = 4096 # 4KB 块
while True:
chunk = source_file.read(chunk_size)
if not chunk:
break
dest_file.write(chunk)
print(f"文件拷贝成功: {source_path} -> {destination_path}")
return True
except Exception as e:
print(f"文件拷贝失败: {e}")
return False
# 测试文件拷贝
if __name__ == "__main__":
# 创建测试文件
with open("source.txt", "w", encoding="utf-8") as f:
f.write("This is a test file for copying.\n")
f.write("It contains multiple lines.\n")
f.write("Line 3\n")
f.write("Line 4\n")
# 拷贝文件
copy_file("source.txt", "destination.txt")
# 验证拷贝结果
print("\n源文件内容:")
with open("source.txt", "r", encoding="utf-8") as f:
print(f.read())
print("\n目标文件内容:")
with open("destination.txt", "r", encoding="utf-8") as f:
print(f.read())第 6 章 面向对象之类和对象
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它将数据和操作数据的方法封装在一起,形成对象。Python 是一种面向对象的编程语言,支持类、对象、继承、多态等面向对象的特性。
6.1 面向过程和面向对象
面向过程编程:
- 以过程为中心,将问题分解为一系列步骤
- 关注的是"如何做",通过函数实现各个步骤
- 数据和操作数据的函数是分离的
面向对象编程:
- 以对象为中心,将问题分解为对象
- 关注的是"做什么",通过对象的方法实现功能
- 数据和操作数据的方法是封装在一起的
示例对比:
python
# 面向过程编程
def calculate_area(length, width):
"""计算矩形面积"""
return length * width
def calculate_perimeter(length, width):
"""计算矩形周长"""
return 2 * (length + width)
# 使用
length = 5
width = 3
area = calculate_area(length, width)
perimeter = calculate_perimeter(length, width)
print(f"面积: {area}, 周长: {perimeter}")
# 面向对象编程
class Rectangle:
"""矩形类"""
def __init__(self, length, width):
self.length = length
self.width = width
def calculate_area(self):
"""计算面积"""
return self.length * self.width
def calculate_perimeter(self):
"""计算周长"""
return 2 * (self.length + self.width)
# 使用
rectangle = Rectangle(5, 3)
area = rectangle.calculate_area()
perimeter = rectangle.calculate_perimeter()
print(f"面积: {area}, 周长: {perimeter}")6.2 类和对象
- 类(Class):是一种抽象的数据类型,定义了对象的属性和方法
- 对象(Object):是类的实例,具有类定义的属性和方法
示例:
python
# 定义类
class Person:
"""人类"""
pass
# 创建对象
person1 = Person()
person2 = Person()
print(type(person1)) # 输出: <class '__main__.Person'>
print(type(person2)) # 输出: <class '__main__.Person'>
print(person1 is person2) # 输出: False(不同的对象)6.3 定义类
在 Python 中,使用 class 关键字来定义类。
语法:
python
class 类名:
"""类的文档字符串"""
# 类属性
# 方法定义示例:
python
class Student:
"""学生类"""
# 类属性(所有实例共享)
school = "Python School"
# 实例方法
def study(self):
"""学习方法"""
print("Studying...")
# 创建对象
student = Student()
print(student.school) # 输出: Python School
student.study() # 输出: Studying...6.4 类的操作
示例:
python
class Student:
"""学生类"""
school = "Python School"
def __init__(self, name, age):
self.name = name
self.age = age
def study(self):
"""学习方法"""
print(f"{self.name} is studying...")
# 创建对象
student1 = Student("Alice", 18)
student2 = Student("Bob", 19)
# 访问属性
print(student1.name) # 输出: Alice
print(student1.age) # 输出: 18
print(student1.school) # 输出: Python School
# 调用方法
student1.study() # 输出: Alice is studying...
student2.study() # 输出: Bob is studying...
# 修改实例属性
student1.age = 20
print(student1.age) # 输出: 20
# 修改类属性(会影响所有实例)
Student.school = "New Python School"
print(student1.school) # 输出: New Python School
print(student2.school) # 输出: New Python School6.5 init()方法
__init__() 方法是类的构造方法,在创建对象时自动调用,用于初始化对象的属性。
示例:
python
class Person:
"""人类"""
def __init__(self, name, age, city):
"""初始化方法"""
self.name = name
self.age = age
self.city = city
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old, from {self.city}.")
# 创建对象时会自动调用 __init__() 方法
person = Person("Alice", 18, "New York")
person.introduce() # 输出: Hello, my name is Alice, I'm 18 years old, from New York.6.6 self
self 是一个特殊参数,在实例方法中表示当前对象本身。当调用实例方法时,Python 会自动将当前对象作为第一个参数传递给方法。
示例:
python
class Car:
"""汽车类"""
def __init__(self, brand, model):
self.brand = brand
self.model = model
def drive(self):
"""驾驶方法"""
print(f"Driving a {self.brand} {self.model}")
# 创建对象
car = Car("Toyota", "Camry")
# 调用方法时,Python 会自动传递 car 作为 self 参数
car.drive() # 等价于 Car.drive(car)
# 输出: Driving a Toyota Camry6.7 属性
6.7.1 类属性
类属性是定义在类中,所有实例共享的属性。
特点:
- 所有实例共享同一个值
- 可以通过类名直接访问
- 可以通过实例访问
- 修改类属性会影响所有实例
示例:
python
class Student:
"""学生类"""
# 类属性
school = "Python School"
student_count = 0
def __init__(self, name, age):
self.name = name
self.age = age
# 每创建一个实例,学生数量加1
Student.student_count += 1
# 访问类属性
print(Student.school) # 输出: Python School
print(Student.student_count) # 输出: 0
# 创建对象
student1 = Student("Alice", 18)
student2 = Student("Bob", 19)
# 通过实例访问类属性
print(student1.school) # 输出: Python School
print(student2.school) # 输出: Python School
# 查看学生数量
print(Student.student_count) # 输出: 2
# 修改类属性
Student.school = "New Python School"
print(student1.school) # 输出: New Python School
print(student2.school) # 输出: New Python School6.7.2 实例属性
实例属性是定义在 __init__() 方法中,每个实例独有的属性。
特点:
- 每个实例有自己的值
- 只能通过实例访问
- 修改实例属性不会影响其他实例
示例:
python
class Person:
"""人类"""
def __init__(self, name, age, city):
# 实例属性
self.name = name
self.age = age
self.city = city
# 创建对象
person1 = Person("Alice", 18, "New York")
person2 = Person("Bob", 19, "London")
# 访问实例属性
print(person1.name) # 输出: Alice
print(person2.name) # 输出: Bob
# 修改实例属性
person1.age = 20
print(person1.age) # 输出: 20
print(person2.age) # 输出: 19(不受影响)
# 为实例添加新属性
person1.gender = "Female"
print(person1.gender) # 输出: Female
# print(person2.gender) # 会报错,因为person2没有gender属性6.8 方法
6.8.1 实例方法
实例方法是最常见的方法类型,需要通过对象调用,第一个参数是 self。
特点:
- 第一个参数是
self,表示当前对象 - 可以访问和修改实例属性
- 可以访问类属性
- 需要通过对象调用
示例:
python
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
# 实例方法
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old.")
# 实例方法修改属性
def celebrate_birthday(self):
"""庆祝生日"""
self.age += 1
print(f"Happy birthday! Now I'm {self.age} years old.")
# 创建对象
person = Person("Alice", 18)
# 调用实例方法
person.introduce() # 输出: Hello, my name is Alice, I'm 18 years old.
person.celebrate_birthday() # 输出: Happy birthday! Now I'm 19 years old.
person.introduce() # 输出: Hello, my name is Alice, I'm 19 years old.6.8.2 类方法
类方法使用 @classmethod 装饰器定义,第一个参数是 cls。
特点:
- 第一个参数是
cls,表示当前类 - 可以访问和修改类属性
- 不能访问实例属性
- 可以通过类名或对象调用
示例:
python
class Company:
"""公司类"""
# 类属性
company_name = "Python Inc."
employee_count = 0
def __init__(self, name, position):
self.name = name
self.position = position
Company.employee_count += 1
# 类方法
@classmethod
def change_company_name(cls, new_name):
"""修改公司名称"""
cls.company_name = new_name
print(f"Company name changed to: {cls.company_name}")
# 类方法
@classmethod
def get_employee_count(cls):
"""获取员工数量"""
return cls.employee_count
# 使用类方法修改公司名称
Company.change_company_name("New Python Inc.")
# 创建员工
emp1 = Company("Alice", "Developer")
emp2 = Company("Bob", "Manager")
# 通过类名调用类方法
print(f"Employee count: {Company.get_employee_count()}") # 输出: Employee count: 2
# 通过对象调用类方法
print(f"Employee count: {emp1.get_employee_count()}") # 输出: Employee count: 26.8.3 静态方法
静态方法使用 @staticmethod 装饰器定义,没有特殊参数。
特点:
- 没有特殊参数
- 不能访问实例属性
- 不能访问类属性(除非通过类名)
- 可以通过类名或对象调用
- 通常用于工具函数
示例:
python
class Calculator:
"""计算器类"""
# 静态方法
@staticmethod
def add(a, b):
"""加法"""
return a + b
# 静态方法
@staticmethod
def subtract(a, b):
"""减法"""
return a - b
# 静态方法
@staticmethod
def multiply(a, b):
"""乘法"""
return a * b
# 静态方法
@staticmethod
def divide(a, b):
"""除法"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# 通过类名调用静态方法
print(Calculator.add(5, 3)) # 输出: 8
print(Calculator.divide(10, 2)) # 输出: 5.0
# 通过对象调用静态方法
calc = Calculator()
print(calc.multiply(4, 6)) # 输出: 246.8.4 在类外定义方法
可以在类外定义方法,然后将其绑定到类或实例。
示例:
python
# 在类外定义函数
def outside_method(self):
"""外部方法"""
print(f"This is an outside method called by {self.name}")
# 定义类
class Person:
"""人类"""
def __init__(self, name):
self.name = name
# 创建对象
person = Person("Alice")
# 将外部函数绑定为实例方法
import types
person.outside_method = types.MethodType(outside_method, person)
person.outside_method() # 输出: This is an outside method called by Alice
# 将外部函数绑定为类方法
def outside_class_method(cls):
"""外部类方法"""
print(f"This is an outside class method of {cls.__name__}")
Person.outside_class_method = classmethod(outside_class_method)
Person.outside_class_method() # 输出: This is an outside class method of Person
# 将外部函数绑定为静态方法
def outside_static_method():
"""外部静态方法"""
print("This is an outside static method")
Person.outside_static_method = staticmethod(outside_static_method)
Person.outside_static_method() # 输出: This is an outside static method6.8.5 特殊方法
特殊方法(魔术方法)是Python中以双下划线开头和结尾的方法,用于实现特定的功能。
常用特殊方法:
| 方法名 | 描述 |
|---|---|
__init__ | 初始化方法 |
__str__ | 字符串表示方法 |
__repr__ | repr 表示方法 |
__eq__ | 相等性比较 |
__lt__ | 小于比较 |
__gt__ | 大于比较 |
__add__ | 加法运算 |
__sub__ | 减法运算 |
__len__ | 长度计算 |
__getitem__ | 索引访问 |
__setitem__ | 索引赋值 |
__delitem__ | 索引删除 |
示例:
python
class Point:
"""点类"""
def __init__(self, x, y):
self.x = x
self.y = y
# 字符串表示方法
def __str__(self):
"""返回友好的字符串表示"""
return f"Point({self.x}, {self.y})"
# repr 表示方法
def __repr__(self):
"""返回正式的字符串表示"""
return f"Point({self.x}, {self.y})"
# 加法运算
def __add__(self, other):
"""点的加法"""
return Point(self.x + other.x, self.y + other.y)
# 相等性比较
def __eq__(self, other):
"""点的相等性比较"""
return self.x == other.x and self.y == other.y
# 长度计算(距离原点的距离)
def __len__(self):
"""返回点到原点的距离的整数部分"""
import math
return int(math.sqrt(self.x ** 2 + self.y ** 2))
# 创建点
p1 = Point(1, 2)
p2 = Point(3, 4)
# 测试 __str__ 和 __repr__
print(p1) # 输出: Point(1, 2)
print(repr(p1)) # 输出: Point(1, 2)
# 测试 __add__
p3 = p1 + p2
print(p3) # 输出: Point(4, 6)
# 测试 __eq__
p4 = Point(1, 2)
print(p1 == p4) # 输出: True
print(p1 == p2) # 输出: False
# 测试 __len__
print(len(p1)) # 输出: 2(sqrt(1+4)=sqrt(5)≈2.236,取整为2)6.9 动态添加属性与方法
Python 允许在运行时动态地为对象和类添加属性和方法。
6.9.1 动态给对象添加属性
示例:
python
class Person:
"""人类"""
def __init__(self, name):
self.name = name
# 创建对象
person = Person("Alice")
# 动态添加实例属性
person.age = 18
person.city = "New York"
person.gender = "Female"
# 访问动态添加的属性
print(f"Name: {person.name}") # 输出: Name: Alice
print(f"Age: {person.age}") # 输出: Age: 18
print(f"City: {person.city}") # 输出: City: New York
print(f"Gender: {person.gender}") # 输出: Gender: Female
# 注意:动态添加的属性只属于当前实例,其他实例没有这些属性
person2 = Person("Bob")
print(hasattr(person2, "age")) # 输出: False6.9.2 动态给类添加属性
示例:
python
class Person:
"""人类"""
def __init__(self, name):
self.name = name
# 动态添加类属性
Person.species = "Human"
Person.max_age = 120
# 通过类访问类属性
print(f"Species: {Person.species}") # 输出: Species: Human
print(f"Max age: {Person.max_age}") # 输出: Max age: 120
# 通过实例访问类属性
person = Person("Alice")
print(f"Person species: {person.species}") # 输出: Person species: Human
print(f"Person max age: {person.max_age}") # 输出: Person max age: 120
# 修改类属性
Person.species = "Homo sapiens"
print(f"Updated species: {Person.species}") # 输出: Updated species: Homo sapiens
print(f"Person species: {person.species}") # 输出: Person species: Homo sapiens6.9.3 动态给实例添加方法
示例:
python
import types
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
# 创建对象
person = Person("Alice", 18)
# 定义要添加的方法
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old.")
def celebrate_birthday(self):
"""庆祝生日"""
self.age += 1
print(f"Happy birthday! Now I'm {self.age} years old.")
# 动态添加实例方法
person.introduce = types.MethodType(introduce, person)
person.celebrate_birthday = types.MethodType(celebrate_birthday, person)
# 调用动态添加的方法
person.introduce() # 输出: Hello, my name is Alice, I'm 18 years old.
person.celebrate_birthday() # 输出: Happy birthday! Now I'm 19 years old.
person.introduce() # 输出: Hello, my name is Alice, I'm 19 years old.
# 注意:动态添加的方法只属于当前实例,其他实例没有这些方法
person2 = Person("Bob", 20)
# person2.introduce() # 会报错,因为person2没有introduce方法6.9.4 动态给类添加方法
示例:
python
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
# 定义要添加的方法
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old.")
# 动态添加实例方法
Person.introduce = introduce
# 定义要添加的类方法
@classmethod
def get_class_info(cls):
"""获取类信息"""
print(f"Class name: {cls.__name__}")
# 动态添加类方法
Person.get_class_info = get_class_info
# 定义要添加的静态方法
@staticmethod
def greet():
"""问候"""
print("Hello, welcome to Python!")
# 动态添加静态方法
Person.greet = greet
# 创建对象
person = Person("Alice", 18)
# 调用动态添加的实例方法
person.introduce() # 输出: Hello, my name is Alice, I'm 18 years old.
# 调用动态添加的类方法
Person.get_class_info() # 输出: Class name: Person
# 调用动态添加的静态方法
Person.greet() # 输出: Hello, welcome to Python!6.9.5 动态删除属性与方法
示例:
python
class Person:
"""人类"""
species = "Human"
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old.")
# 创建对象
person = Person("Alice", 18)
# 动态添加属性
person.city = "New York"
print(f"City: {person.city}") # 输出: City: New York
# 删除实例属性
del person.city
# print(person.city) # 会报错,因为city属性已被删除
print(hasattr(person, "city")) # 输出: False
# 删除实例方法
print(hasattr(person, "introduce")) # 输出: True
del person.introduce
# person.introduce() # 会报错,因为introduce方法已被删除
print(hasattr(person, "introduce")) # 输出: False
# 删除类属性
print(f"Species: {Person.species}") # 输出: Species: Human
del Person.species
# print(Person.species) # 会报错,因为species属性已被删除
print(hasattr(Person, "species")) # 输出: False6.9.6 slots限制实例属性与实例方法
__slots__ 是一个特殊属性,用于限制类的实例可以拥有的属性和方法。
特点:
- 可以提高内存使用效率
- 可以限制实例可以添加的属性
- 定义后,实例只能拥有
__slots__中指定的属性
示例:
python
class Person:
"""人类"""
# 限制实例只能拥有name和age属性
__slots__ = ["name", "age"]
def __init__(self, name, age):
self.name = name
self.age = age
# 创建对象
person = Person("Alice", 18)
# 访问已定义的属性
print(f"Name: {person.name}") # 输出: Name: Alice
print(f"Age: {person.age}") # 输出: Age: 18
# 尝试添加新属性(会报错)
try:
person.city = "New York"
except AttributeError as e:
print(f"Error: {e}") # 输出: Error: 'Person' object has no attribute 'city'
# 注意:__slots__ 只限制实例属性,不限制类属性
Person.species = "Human"
print(f"Species: {Person.species}") # 输出: Species: Human
# 注意:子类默认会继承父类的 __slots__,但可以扩展
class Student(Person):
"""学生类"""
# 扩展 __slots__
__slots__ = ["student_id"]
def __init__(self, name, age, student_id):
super().__init__(name, age)
self.student_id = student_id
# 创建学生对象
student = Student("Bob", 19, "S12345")
print(f"Name: {student.name}") # 输出: Name: Bob
print(f"Age: {student.age}") # 输出: Age: 19
print(f"Student ID: {student.student_id}") # 输出: Student ID: S12345
# 尝试添加新属性(会报错)
try:
student.city = "London"
except AttributeError as e:
print(f"Error: {e}") # 输出: Error: 'Student' object has no attribute 'city'第 7 章 面向对象之三大特性
面向对象编程的三大特性是封装、继承和多态。这三个特性是面向对象编程的核心,它们使得代码更加模块化、可重用和可维护。
7.1 封装
封装是指将数据和操作数据的方法封装在一起,对外部隐藏实现细节,只提供公共的接口。
7.1.1 私有化
在 Python 中,通过在属性或方法名前添加双下划线 __ 来实现私有化。私有化后的属性或方法在类外部不能直接访问。
示例:
python
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name # 公共属性
self.__age = age # 私有属性
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.__age} years old.")
# 创建对象
person = Person("Alice", 18)
# 访问公共属性
print(person.name) # 输出: Alice
# 尝试访问私有属性(会报错)
try:
print(person.__age)
except AttributeError as e:
print(f"Error: {e}") # 输出: Error: 'Person' object has no attribute '__age'
# 通过公共方法访问私有属性
person.introduce() # 输出: Hello, my name is Alice, I'm 18 years old.7.1.2 私有属性
私有属性是指在类内部可以访问,但在类外部不能直接访问的属性。
示例:
python
class BankAccount:
"""银行账户类"""
def __init__(self, account_number, balance):
self.account_number = account_number # 公共属性
self.__balance = balance # 私有属性
def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
print(f"Deposit successful. New balance: {self.__balance}")
else:
print("Deposit amount must be positive.")
def withdraw(self, amount):
"""取款"""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrawal successful. New balance: {self.__balance}")
else:
print("Invalid withdrawal amount.")
def get_balance(self):
"""获取余额"""
return self.__balance
# 创建账户
account = BankAccount("123456789", 1000)
# 访问公共属性
print(account.account_number) # 输出: 123456789
# 尝试直接访问私有属性(会报错)
try:
print(account.__balance)
except AttributeError as e:
print(f"Error: {e}") # 输出: Error: 'BankAccount' object has no attribute '__balance'
# 通过公共方法操作私有属性
account.deposit(500) # 输出: Deposit successful. New balance: 1500
account.withdraw(200) # 输出: Withdrawal successful. New balance: 1300
print(f"Current balance: {account.get_balance()}") # 输出: Current balance: 13007.1.3 私有方法
私有方法是指在类内部可以调用,但在类外部不能直接调用的方法。
示例:
python
class Calculator:
"""计算器类"""
def __init__(self):
pass
def add(self, a, b):
"""加法"""
return a + b
def multiply(self, a, b):
"""乘法"""
return a * b
def calculate(self, operation, a, b):
"""计算"""
if operation == "add":
return self.add(a, b)
elif operation == "multiply":
return self.multiply(a, b)
else:
return self.__invalid_operation()
def __invalid_operation(self):
"""无效操作(私有方法)"""
print("Invalid operation.")
return None
# 创建计算器
calc = Calculator()
# 调用公共方法
print(calc.add(5, 3)) # 输出: 8
print(calc.multiply(4, 6)) # 输出: 24
print(calc.calculate("add", 10, 20)) # 输出: 30
print(calc.calculate("divide", 10, 2)) # 输出: Invalid operation. None
# 尝试调用私有方法(会报错)
try:
calc.__invalid_operation()
except AttributeError as e:
print(f"Error: {e}") # 输出: Error: 'Calculator' object has no attribute '__invalid_operation'7.1.4 property
property 是一个装饰器,用于将方法转换为属性,使得可以像访问属性一样访问方法。
示例:
python
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.__age = age
@property
def age(self):
"""获取年龄"""
return self.__age
@age.setter
def age(self, value):
"""设置年龄"""
if value >= 0:
self.__age = value
else:
print("Age must be non-negative.")
@property
def is_adult(self):
"""是否成年"""
return self.__age >= 18
# 创建对象
person = Person("Alice", 18)
# 使用 property 访问私有属性
print(person.age) # 输出: 18
print(person.is_adult) # 输出: True
# 使用 setter 修改私有属性
person.age = 20
print(person.age) # 输出: 20
# 尝试设置无效值
person.age = -5 # 输出: Age must be non-negative.
print(person.age) # 输出: 20(保持不变)7.2 继承
继承是指一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码的重用。
7.2.1 单继承
单继承是指一个子类只继承一个父类。
示例:
python
class Animal:
"""动物类"""
def __init__(self, name):
self.name = name
def eat(self):
"""吃"""
print(f"{self.name} is eating.")
def sleep(self):
"""睡"""
print(f"{self.name} is sleeping.")
class Dog(Animal):
"""狗类"""
def __init__(self, name, breed):
super().__init__(name) # 调用父类的 __init__ 方法
self.breed = breed
def bark(self):
"""叫"""
print(f"{self.name} is barking.")
# 创建对象
dog = Dog("Buddy", "Labrador")
# 访问继承的属性和方法
print(dog.name) # 输出: Buddy
print(dog.breed) # 输出: Labrador
dog.eat() # 输出: Buddy is eating.
dog.sleep() # 输出: Buddy is sleeping.
dog.bark() # 输出: Buddy is barking.7.2.2 多继承
多继承是指一个子类继承多个父类。
示例:
python
class Animal:
"""动物类"""
def __init__(self, name):
self.name = name
def eat(self):
"""吃"""
print(f"{self.name} is eating.")
class Pet:
"""宠物类"""
def __init__(self, owner):
self.owner = owner
def play(self):
"""玩"""
print(f"The pet is playing with {self.owner}.")
class Dog(Animal, Pet):
"""狗类"""
def __init__(self, name, owner, breed):
Animal.__init__(self, name) # 调用 Animal 的 __init__ 方法
Pet.__init__(self, owner) # 调用 Pet 的 __init__ 方法
self.breed = breed
def bark(self):
"""叫"""
print(f"{self.name} is barking.")
# 创建对象
dog = Dog("Buddy", "Alice", "Labrador")
# 访问继承的属性和方法
print(dog.name) # 输出: Buddy
print(dog.owner) # 输出: Alice
print(dog.breed) # 输出: Labrador
dog.eat() # 输出: Buddy is eating.
dog.play() # 输出: The pet is playing with Alice.
dog.bark() # 输出: Buddy is barking.7.2.3 复用父类方法
示例:
python
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
"""自我介绍"""
print(f"Hello, my name is {self.name}, I'm {self.age} years old.")
class Student(Person):
"""学生类"""
def __init__(self, name, age, student_id):
super().__init__(name, age) # 复用父类的 __init__ 方法
self.student_id = student_id
def introduce(self):
"""自我介绍(重写父类方法)"""
super().introduce() # 复用父类的 introduce 方法
print(f"My student ID is {self.student_id}.")
# 创建对象
student = Student("Alice", 18, "S12345")
student.introduce()
# 输出:
# Hello, my name is Alice, I'm 18 years old.
# My student ID is S12345.7.2.4 方法解析顺序
方法解析顺序(Method Resolution Order,MRO)是指在多继承情况下,Python 查找方法的顺序。
示例:
python
class A:
"""类 A"""
def method(self):
print("A.method()")
class B(A):
"""类 B"""
def method(self):
print("B.method()")
class C(A):
"""类 C"""
def method(self):
print("C.method()")
class D(B, C):
"""类 D"""
pass
# 创建对象
d = D()
d.method() # 输出: B.method()
# 查看方法解析顺序
print(D.__mro__) # 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)7.2.5 方法重写
方法重写是指子类重新实现父类的方法,以满足子类的特定需求。
示例:
python
class Animal:
"""动物类"""
def make_sound(self):
"""发出声音"""
print("Some generic sound")
class Dog(Animal):
"""狗类"""
def make_sound(self):
"""发出声音(重写父类方法)"""
print("Woof! Woof!")
class Cat(Animal):
"""猫类"""
def make_sound(self):
"""发出声音(重写父类方法)"""
print("Meow! Meow!")
# 创建对象
dog = Dog()
cat = Cat()
# 调用重写的方法
dog.make_sound() # 输出: Woof! Woof!
cat.make_sound() # 输出: Meow! Meow!7.3 多态
多态是指不同类型的对象可以通过相同的接口调用不同的实现。
示例:
python
class Animal:
"""动物类"""
def make_sound(self):
"""发出声音"""
raise NotImplementedError("Subclass must implement abstract method")
class Dog(Animal):
"""狗类"""
def make_sound(self):
"""发出声音"""
print("Woof! Woof!")
class Cat(Animal):
"""猫类"""
def make_sound(self):
"""发出声音"""
print("Meow! Meow!")
class Cow(Animal):
"""牛类"""
def make_sound(self):
"""发出声音"""
print("Moo! Moo!")
# 多态函数
def animal_sound(animal):
"""动物发声"""
animal.make_sound()
# 创建对象
dog = Dog()
cat = Cat()
cow = Cow()
# 调用多态函数
animal_sound(dog) # 输出: Woof! Woof!
animal_sound(cat) # 输出: Meow! Meow!
animal_sound(cow) # 输出: Moo! Moo!
# 多态列表
animals = [dog, cat, cow]
for animal in animals:
animal.make_sound()
# 输出:
# Woof! Woof!
# Meow! Meow!
# Moo! Moo!第 9 章 错误和异常
错误和异常是程序运行过程中可能遇到的问题。Python 提供了异常处理机制,使得程序在遇到错误时能够优雅地处理,而不是直接崩溃。
9.1 异常介绍
9.1.1 语法错误
语法错误是指代码不符合 Python 的语法规则,在程序执行前就会被检测到。
示例:
python
# 语法错误示例
print("Hello world" # 缺少右括号
for i in range(5): # 缺少冒号
print(i)常见语法错误:
- 缺少括号、引号或冒号
- 缩进错误
- 语法结构不正确
9.1.2 异常
异常是指程序在运行过程中遇到的错误,如除零错误、索引越界等。
示例:
python
# 异常示例
# 除零错误
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}") # 输出: Error: division by zero
# 索引越界错误
try:
lst = [1, 2, 3]
print(lst[5])
except IndexError as e:
print(f"Error: {e}") # 输出: Error: list index out of range
# 类型错误
try:
result = "10" + 5
except TypeError as e:
print(f"Error: {e}") # 输出: Error: can only concatenate str (not "int") to str9.2 异常处理 try except
9.2.1 try except
try except 语句用于捕获和处理异常。
语法:
python
try:
# 可能引发异常的代码
except 异常类型:
# 异常处理代码示例:
python
try:
num1 = int(input("请输入第一个数: "))
num2 = int(input("请输入第二个数: "))
result = num1 / num2
print(f"结果: {result}")
except:
print("发生了错误")9.2.2 捕获指定类型的异常以及获取异常信息
示例:
python
try:
num1 = int(input("请输入第一个数: "))
num2 = int(input("请输入第二个数: "))
result = num1 / num2
print(f"结果: {result}")
except ValueError as e:
print(f"值错误: {e}")
except ZeroDivisionError as e:
print(f"除零错误: {e}")
except Exception as e:
print(f"其他错误: {e}")9.2.3 else
else 子句在 try 块没有引发异常时执行。
示例:
python
try:
num1 = int(input("请输入第一个数: "))
num2 = int(input("请输入第二个数: "))
result = num1 / num2
except ValueError as e:
print(f"值错误: {e}")
except ZeroDivisionError as e:
print(f"除零错误: {e}")
else:
print(f"结果: {result}")
print("计算成功!")9.2.4 finally
finally 子句无论是否发生异常都会执行,通常用于释放资源。
示例:
python
try:
file = open("example.txt", "r")
content = file.read()
print(content)
except FileNotFoundError as e:
print(f"文件不存在: {e}")
finally:
# 无论是否发生异常,都会执行这里的代码
if 'file' in locals():
file.close()
print("文件已关闭")9.3 抛出异常 raise
raise 语句用于手动抛出异常。
语法:
python
raise 异常类型(异常信息)示例:
python
def divide(a, b):
if b == 0:
raise ZeroDivisionError("除数不能为零")
return a / b
try:
result = divide(10, 0)
print(f"结果: {result}")
except ZeroDivisionError as e:
print(f"错误: {e}") # 输出: 错误: 除数不能为零9.3.1 assert断言
assert 语句用于检查条件是否为真,如果为假则抛出 AssertionError 异常。
语法:
python
assert 条件, 错误信息示例:
python
def calculate_age(year):
assert year > 0, "年份必须大于0"
current_year = 2023
return current_year - year
try:
age = calculate_age(-2000)
print(f"年龄: {age}")
except AssertionError as e:
print(f"断言错误: {e}") # 输出: 断言错误: 年份必须大于0
# 断言在生产环境中可以被禁用
# python -O script.py # -O 选项会禁用断言9.4 自定义异常
自定义异常是指用户自己定义的异常类,通常继承自 Exception 类。
示例:
python
class NegativeAgeError(Exception):
"""年龄为负数的异常"""
def __init__(self, age):
self.age = age
super().__init__(f"年龄不能为负数: {age}")
class AgeTooLargeError(Exception):
"""年龄过大的异常"""
def __init__(self, age, max_age=150):
self.age = age
self.max_age = max_age
super().__init__(f"年龄不能超过 {max_age}: {age}")
def set_age(age):
if age < 0:
raise NegativeAgeError(age)
if age > 150:
raise AgeTooLargeError(age)
print(f"年龄设置为: {age}")
try:
set_age(-10)
except NegativeAgeError as e:
print(f"错误: {e}") # 输出: 错误: 年龄不能为负数: -10
try:
set_age(200)
except AgeTooLargeError as e:
print(f"错误: {e}") # 输出: 错误: 年龄不能超过 150: 2009.5 异常的传递
异常的传递是指当一个函数中发生异常时,如果没有被捕获,异常会向上传递到调用该函数的地方。
示例:
python
def func3():
print("func3 开始")
result = 10 / 0 # 引发异常
print("func3 结束")
def func2():
print("func2 开始")
func3() # 调用 func3,异常会传递到这里
print("func2 结束")
def func1():
print("func1 开始")
try:
func2() # 调用 func2,异常会传递到这里
except ZeroDivisionError as e:
print(f"捕获到异常: {e}")
print("func1 结束")
# 调用 func1
func1()
# 输出:
# func1 开始
# func2 开始
# func3 开始
# 捕获到异常: division by zero
# func1 结束9.6 with关键字
with 关键字用于管理资源,确保资源在使用后被正确释放,即使发生异常。
9.6.1 语法
语法:
python
with 表达式 as 变量:
# 使用资源的代码9.6.2 工作原理
with 语句会在进入代码块前调用对象的 __enter__ 方法,在离开代码块时调用 __exit__ 方法,无论是否发生异常。
示例:
python
class Resource:
"""资源类"""
def __enter__(self):
print("获取资源")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("释放资源")
# 如果返回 True,则抑制异常
return False
def do_something(self):
print("使用资源")
# 使用 with 语句
with Resource() as res:
res.do_something()
# 这里可以发生异常
# raise Exception("测试异常")
# 输出:
# 获取资源
# 使用资源
# 释放资源9.6.3 案例:打开一个文件并向其中写入...
示例:
python
# 使用 with 语句操作文件
with open("example.txt", "w", encoding="utf-8") as file:
file.write("Hello, World!\n")
file.write("Python is great!\n")
file.write("Exception handling is important!\n")
print("文件写入完成")
# 读取文件内容
with open("example.txt", "r", encoding="utf-8") as file:
content = file.read()
print("文件内容:")
print(content)
# 输出:
# 文件写入完成
# 文件内容:
# Hello, World!
# Python is great!
# Exception handling is important!9.7 Python常见异常
9.7.1 异常基类
- BaseException:所有异常的基类
- Exception:所有非系统退出类异常的基类
- ArithmeticError:算术错误的基类
- LookupError:查找错误的基类
- SyntaxError:语法错误
- ValueError:值错误
- TypeError:类型错误
9.7.2 具体异常
| 异常名称 | 描述 |
|---|---|
ZeroDivisionError | 除零错误 |
IndexError | 索引越界错误 |
KeyError | 字典键不存在错误 |
ValueError | 值错误 |
TypeError | 类型错误 |
NameError | 名称不存在错误 |
FileNotFoundError | 文件不存在错误 |
IOError | I/O 错误 |
ImportError | 导入错误 |
ModuleNotFoundError | 模块不存在错误 |
AttributeError | 属性不存在错误 |
AssertionError | 断言错误 |
RuntimeError | 运行时错误 |
NotImplementedError | 未实现错误 |
KeyboardInterrupt | 用户中断错误(Ctrl+C) |
示例:
python
# 捕获多种异常
try:
# 可能引发的异常
# 1. 除零错误
# result = 10 / 0
# 2. 索引越界错误
# lst = [1, 2, 3]
# print(lst[5])
# 3. 键不存在错误
# d = {"a": 1, "b": 2}
# print(d["c"])
# 4. 值错误
# num = int("abc")
# 5. 类型错误
# result = "10" + 5
# 6. 名称错误
# print(undefined_variable)
# 7. 文件不存在错误
# with open("non_existent_file.txt", "r") as f:
# pass
# 8. 属性错误
# class A:
# pass
# a = A()
# print(a.non_existent_attribute)
# 没有异常
print("一切正常")
except ZeroDivisionError as e:
print(f"除零错误: {e}")
except IndexError as e:
print(f"索引越界错误: {e}")
except KeyError as e:
print(f"键不存在错误: {e}")
except ValueError as e:
print(f"值错误: {e}")
except TypeError as e:
print(f"类型错误: {e}")
except NameError as e:
print(f"名称错误: {e}")
except FileNotFoundError as e:
print(f"文件不存在错误: {e}")
except AttributeError as e:
print(f"属性错误: {e}")
except Exception as e:
print(f"其他错误: {e}")第 10 章 模块与包
模块与包是 Python 中组织代码的重要方式。模块是一个包含 Python 代码的文件,包是一个包含多个模块的目录。它们使得代码更加模块化、可重用和可维护。
10.1 模块概述
模块是一个包含 Python 定义和语句的文件,文件名就是模块名加上 .py 后缀。
模块的作用:
- 组织代码,提高代码的可维护性
- 实现代码的重用
- 避免命名冲突
10.2 创建模块
示例:
创建一个名为 mymodule.py 的文件:
python
# mymodule.py
# 变量
PI = 3.14159
# 函数
def greet(name):
"""问候"""
return f"Hello, {name}!"
def calculate_area(radius):
"""计算圆的面积"""
return PI * radius ** 2
# 类
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
"""自我介绍"""
return f"I'm {self.name}, {self.age} years old."10.3 导入模块
10.3.1 全部导入 import
示例:
python
# 导入整个模块
import mymodule
# 使用模块中的变量
print(mymodule.PI) # 输出: 3.14159
# 使用模块中的函数
print(mymodule.greet("Alice")) # 输出: Hello, Alice!
print(mymodule.calculate_area(5)) # 输出: 78.53975
# 使用模块中的类
person = mymodule.Person("Bob", 18)
print(person.introduce()) # 输出: I'm Bob, 18 years old.
# 导入模块并指定别名
import mymodule as mm
print(mm.PI) # 输出: 3.1415910.3.2 局部导入 from import
示例:
python
# 导入模块中的特定内容
from mymodule import PI, greet, Person
# 直接使用导入的内容
print(PI) # 输出: 3.14159
print(greet("Alice")) # 输出: Hello, Alice!
person = Person("Bob", 18)
print(person.introduce()) # 输出: I'm Bob, 18 years old.
# 导入并指定别名
from mymodule import calculate_area as calc_area
print(calc_area(5)) # 输出: 78.5397510.3.3 局部导入 from import *
示例:
python
# 导入模块中的所有内容
from mymodule import *
# 直接使用所有导入的内容
print(PI) # 输出: 3.14159
print(greet("Alice")) # 输出: Hello, Alice!
print(calculate_area(5)) # 输出: 78.53975
person = Person("Bob", 18)
print(person.introduce()) # 输出: I'm Bob, 18 years old.
# 注意:不推荐使用这种方式,因为可能会导致命名冲突10.3.4 模块搜索顺序
Python 导入模块时的搜索顺序:
- 当前目录
- 环境变量
PYTHONPATH中的目录 - Python 标准库目录
- 任何 .pth 文件中指定的目录
示例:
python
import sys
# 查看模块搜索路径
print(sys.path)
# 添加自定义搜索路径
sys.path.append("/path/to/modules")10.3.5 all
__all__ 是一个列表,用于指定使用 from module import * 时导入的内容。
示例:
修改 mymodule.py 文件:
python
# mymodule.py
# 变量
PI = 3.14159
# 函数
def greet(name):
"""问候"""
return f"Hello, {name}!"
def calculate_area(radius):
"""计算圆的面积"""
return PI * radius ** 2
def internal_function():
"""内部函数"""
return "This is an internal function"
# 类
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
"""自我介绍"""
return f"I'm {self.name}, {self.age} years old."
# 指定可导出的内容
__all__ = ["PI", "greet", "Person"]现在使用 from mymodule import * 只会导入 PI、greet 和 Person:
python
from mymodule import *
print(PI) # 输出: 3.14159
print(greet("Alice")) # 输出: Hello, Alice!
person = Person("Bob", 18)
print(person.introduce()) # 输出: I'm Bob, 18 years old.
# print(calculate_area(5)) # 会报错,因为 calculate_area 不在 __all__ 中
# print(internal_function()) # 会报错,因为 internal_function 不在 __all__ 中10.3.6 name
__name__ 是一个特殊变量,用于表示模块的名称。
- 当模块被直接运行时,
__name__的值为"__main__" - 当模块被导入时,
__name__的值为模块名
示例:
修改 mymodule.py 文件,添加测试代码:
python
# mymodule.py
# 变量
PI = 3.14159
# 函数
def greet(name):
"""问候"""
return f"Hello, {name}!"
# 测试代码
if __name__ == "__main__":
print("Testing mymodule...")
print(f"PI = {PI}")
print(greet("Test"))
print("Testing completed.")当直接运行 mymodule.py 时,会执行测试代码:
bash
python mymodule.py
# 输出:
# Testing mymodule...
# PI = 3.14159
# Hello, Test!
# Testing completed.当导入 mymodule 时,不会执行测试代码:
python
import mymodule
# 不会输出测试信息10.4 dir()
dir() 函数用于查看模块或对象的属性和方法。
示例:
python
import mymodule
# 查看模块的属性和方法
print(dir(mymodule))
# 查看内置模块的属性和方法
import math
print(dir(math))
# 查看对象的属性和方法
person = mymodule.Person("Alice", 18)
print(dir(person))10.5 包概述
包是一个包含多个模块的目录,目录中必须包含一个 __init__.py 文件。
包的作用:
- 组织多个相关的模块
- 避免模块名冲突
- 提供命名空间
10.6 创建包
示例:
创建一个名为 mypackage 的包:
- 创建
mypackage目录 - 在
mypackage目录中创建__init__.py文件 - 在
mypackage目录中创建多个模块文件
目录结构:
mypackage/
├── __init__.py
├── module1.py
└── module2.py__init__.py 文件:
python
# mypackage/__init__.py
# 包的文档字符串
"""My package"""
# 导入模块
from . import module1
from . import module2
# 或直接导入模块中的内容
from .module1 import function1
from .module2 import function2
# 指定可导出的内容
__all__ = ["module1", "module2", "function1", "function2"]module1.py 文件:
python
# mypackage/module1.py
def function1():
"""函数1"""
return "This is function 1"
class Class1:
"""类1"""
def method1(self):
return "This is method 1"module2.py 文件:
python
# mypackage/module2.py
def function2():
"""函数2"""
return "This is function 2"
class Class2:
"""类2"""
def method2(self):
return "This is method 2"10.7 导入包
10.7.1 全局导入 import
示例:
python
# 导入整个包
import mypackage
# 使用包中的模块
print(mypackage.module1.function1()) # 输出: This is function 1
# 使用包中直接导入的内容
print(mypackage.function2()) # 输出: This is function 2
# 导入包并指定别名
import mypackage as mp
print(mp.module1.function1()) # 输出: This is function 110.7.2 局部导入包下的模块 from import
示例:
python
# 导入包中的模块
from mypackage import module1, module2
# 使用模块中的内容
print(module1.function1()) # 输出: This is function 1
print(module2.function2()) # 输出: This is function 2
# 导入模块并指定别名
from mypackage import module1 as m1
print(m1.function1()) # 输出: This is function 110.7.3 局部导入包下模块的成员 from ...
示例:
python
# 导入包中模块的特定内容
from mypackage.module1 import function1, Class1
from mypackage.module2 import function2, Class2
# 直接使用导入的内容
print(function1()) # 输出: This is function 1
print(function2()) # 输出: This is function 2
obj1 = Class1()
print(obj1.method1()) # 输出: This is method 1
obj2 = Class2()
print(obj2.method2()) # 输出: This is method 210.7.4 局部导入包下的模块 from import *(从包中...)
示例:
python
# 导入包中所有可导出的内容
from mypackage import *
# 使用导入的内容
print(function1()) # 输出: This is function 1
print(function2()) # 输出: This is function 2
# 注意:导入的内容由 __init__.py 中的 __all__ 决定10.8 常用标准库
Python 标准库是 Python 内置的模块和包,提供了丰富的功能。
常用标准库:
| 模块名 | 描述 |
|---|---|
os | 操作系统接口 |
sys | Python 解释器相关 |
math | 数学函数 |
random | 随机数生成 |
datetime | 日期和时间处理 |
json | JSON 数据处理 |
re | 正则表达式 |
collections | 集合数据类型 |
itertools | 迭代器工具 |
functools | 函数工具 |
time | 时间相关函数 |
threading | 线程相关 |
multiprocessing | 进程相关 |
socket | 网络套接字 |
http | HTTP 相关 |
示例:
python
# 使用 os 模块
import os
print(os.getcwd()) # 获取当前工作目录
print(os.listdir(".")) # 列出当前目录的文件和目录
# 使用 math 模块
import math
print(math.pi) # 输出: 3.1415926535897693
print(math.sqrt(16)) # 输出: 4.0
# 使用 random 模块
import random
print(random.randint(1, 10)) # 生成 1-10 之间的随机整数
print(random.choice(["apple", "banana", "cherry"])) # 从列表中随机选择一个元素
# 使用 datetime 模块
from datetime import datetime, timedelta
print(datetime.now()) # 当前时间
print(datetime.now() + timedelta(days=1)) # 明天的时间
# 使用 json 模块
import json
data = {"name": "Alice", "age": 18, "city": "New York"}
json_str = json.dumps(data) # 转换为 JSON 字符串
print(json_str) # 输出: {"name": "Alice", "age": 18, "city": "New York"}
parsed_data = json.loads(json_str) # 解析 JSON 字符串
print(parsed_data["name"]) # 输出: Alice10.9 导入第三方库
第三方库是由社区开发的 Python 包,可以通过包管理工具安装。
10.9.1 pip 命令方式
示例:
bash
# 安装第三方库
pip install requests
# 安装特定版本的库
pip install requests==2.31.0
# 升级库
pip install --upgrade requests
# 卸载库
pip uninstall requests
# 查看已安装的库
pip list
# 查看库的详细信息
pip show requests10.9.2 pycharm 中导入
在 PyCharm 中导入第三方库:
- 打开 PyCharm 项目
- 点击
File->Settings->Project: <项目名>->Python Interpreter - 点击
+按钮,搜索并安装需要的库 - 点击
Install Package完成安装
10.10 打包自己的库并安装
步骤:
- 创建项目目录结构
- 编写 setup.py 文件
- 构建包
- 安装包
示例:
目录结构:
myproject/
├── mypackage/
│ ├── __init__.py
│ ├── module1.py
│ └── module2.py
└── setup.pysetup.py 文件:
python
# setup.py
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1.0",
description="My custom package",
author="Your Name",
author_email="your.email@example.com",
packages=find_packages(),
install_requires=[
# 依赖项,例如: "requests>=2.0.0"
],
)构建和安装:
bash
# 构建包
cd myproject
python setup.py sdist bdist_wheel
# 安装包
pip install dist/mypackage-0.1.0.tar.gz
# 或使用开发模式安装(便于开发时修改)
pip install -e .使用安装的包:
python
import mypackage
print(mypackage.module1.function1()) # 输出: This is function 1
print(mypackage.module2.function2()) # 输出: This is function 2第 11 章 Python高级语法
Python高级语法包括一些进阶的编程概念和技术,如浅拷贝与深拷贝、迭代器、生成器、命名空间、作用域、闭包和装饰器等。这些概念和技术可以帮助你编写更加高效、优雅的Python代码。
11.1 浅拷贝与深拷贝
浅拷贝和深拷贝是Python中复制对象的两种方式,它们的区别在于复制的深度不同。
11.1.1 如何浅拷贝
浅拷贝只会复制对象的引用,而不会复制对象本身。当对象包含嵌套对象时,浅拷贝只会复制嵌套对象的引用。
示例:
python
import copy
# 原始列表
lst1 = [1, 2, [3, 4]]
# 浅拷贝
lst2 = copy.copy(lst1)
# 或使用列表的 copy() 方法
# lst2 = lst1.copy()
# 或使用切片
# lst2 = lst1[:]
print(f"原始列表: {lst1}") # 输出: 原始列表: [1, 2, [3, 4]]
print(f"浅拷贝: {lst2}") # 输出: 浅拷贝: [1, 2, [3, 4]]
# 修改原始列表中的嵌套列表
lst1[2][0] = 30
print(f"修改后的原始列表: {lst1}") # 输出: 修改后的原始列表: [1, 2, [30, 4]]
print(f"浅拷贝后的列表: {lst2}") # 输出: 浅拷贝后的列表: [1, 2, [30, 4]]
# 注意:浅拷贝后的列表中的嵌套列表也被修改了11.1.2 案例
示例:
python
import copy
# 原始字典
dict1 = {"name": "Alice", "age": 18, "grades": [85, 90, 95]}
# 浅拷贝
dict2 = copy.copy(dict1)
print(f"原始字典: {dict1}") # 输出: 原始字典: {'name': 'Alice', 'age': 18, 'grades': [85, 90, 95]}
print(f"浅拷贝: {dict2}") # 输出: 浅拷贝: {'name': 'Alice', 'age': 18, 'grades': [85, 90, 95]}
# 修改原始字典中的嵌套列表
dict1["grades"][0] = 100
print(f"修改后的原始字典: {dict1}") # 输出: 修改后的原始字典: {'name': 'Alice', 'age': 18, 'grades': [100, 90, 95]}
print(f"浅拷贝后的字典: {dict2}") # 输出: 浅拷贝后的字典: {'name': 'Alice', 'age': 18, 'grades': [100, 90, 95]}
# 注意:浅拷贝后的字典中的嵌套列表也被修改了11.1.3 如何深拷贝
深拷贝会递归复制对象及其所有嵌套对象,创建一个完全独立的副本。
示例:
python
import copy
# 原始列表
lst1 = [1, 2, [3, 4]]
# 深拷贝
lst2 = copy.deepcopy(lst1)
print(f"原始列表: {lst1}") # 输出: 原始列表: [1, 2, [3, 4]]
print(f"深拷贝: {lst2}") # 输出: 深拷贝: [1, 2, [3, 4]]
# 修改原始列表中的嵌套列表
lst1[2][0] = 30
print(f"修改后的原始列表: {lst1}") # 输出: 修改后的原始列表: [1, 2, [30, 4]]
print(f"深拷贝后的列表: {lst2}") # 输出: 深拷贝后的列表: [1, 2, [3, 4]]
# 注意:深拷贝后的列表中的嵌套列表没有被修改11.1.4 案例
示例:
python
import copy
# 原始字典
dict1 = {"name": "Alice", "age": 18, "grades": [85, 90, 95]}
# 深拷贝
dict2 = copy.deepcopy(dict1)
print(f"原始字典: {dict1}") # 输出: 原始字典: {'name': 'Alice', 'age': 18, 'grades': [85, 90, 95]}
print(f"深拷贝: {dict2}") # 输出: 深拷贝: {'name': 'Alice', 'age': 18, 'grades': [85, 90, 95]}
# 修改原始字典中的嵌套列表
dict1["grades"][0] = 100
print(f"修改后的原始字典: {dict1}") # 输出: 修改后的原始字典: {'name': 'Alice', 'age': 18, 'grades': [100, 90, 95]}
print(f"深拷贝后的字典: {dict2}") # 输出: 深拷贝后的字典: {'name': 'Alice', 'age': 18, 'grades': [85, 90, 95]}
# 注意:深拷贝后的字典中的嵌套列表没有被修改11.1.5 拷贝的特殊情况
示例:
python
import copy
# 不可变对象的拷贝
# 对于不可变对象(如整数、字符串、元组),浅拷贝和深拷贝的效果相同
x = 10
y = copy.copy(x)
z = copy.deepcopy(x)
print(f"x: {x}, y: {y}, z: {z}") # 输出: x: 10, y: 10, z: 10
print(f"x is y: {x is y}") # 输出: x is y: True
print(f"x is z: {x is z}") # 输出: x is z: True
# 包含不可变对象的列表
lst1 = [1, 2, (3, 4)]
lst2 = copy.copy(lst1)
lst3 = copy.deepcopy(lst1)
print(f"原始列表: {lst1}") # 输出: 原始列表: [1, 2, (3, 4)]
print(f"浅拷贝: {lst2}") # 输出: 浅拷贝: [1, 2, (3, 4)]
print(f"深拷贝: {lst3}") # 输出: 深拷贝: [1, 2, (3, 4)]
# 修改原始列表中的元组(元组是不可变的,所以不能修改)
# lst1[2][0] = 30 # 会报错
# 修改原始列表中的普通元素
lst1[0] = 10
print(f"修改后的原始列表: {lst1}") # 输出: 修改后的原始列表: [10, 2, (3, 4)]
print(f"浅拷贝后的列表: {lst2}") # 输出: 浅拷贝后的列表: [1, 2, (3, 4)]
print(f"深拷贝后的列表: {lst3}") # 输出: 深拷贝后的列表: [1, 2, (3, 4)]11.2 迭代器 可迭代对象
11.2.1 使用迭代器
迭代器是一个实现了 __iter__() 和 __next__() 方法的对象,用于遍历可迭代对象。
示例:
python
# 创建一个列表的迭代器
lst = [1, 2, 3, 4, 5]
it = iter(lst)
# 使用 next() 函数获取下一个元素
print(next(it)) # 输出: 1
print(next(it)) # 输出: 2
print(next(it)) # 输出: 3
print(next(it)) # 输出: 4
print(next(it)) # 输出: 5
# print(next(it)) # 会报错,因为没有更多元素
# 使用 for 循环遍历迭代器
lst = [1, 2, 3, 4, 5]
for item in lst:
print(item) # 输出: 1, 2, 3, 4, 5
# for 循环内部会自动调用 iter() 和 next()11.2.2 可迭代对象
可迭代对象是一个实现了 __iter__() 方法的对象,它可以返回一个迭代器。
示例:
python
# 常见的可迭代对象
# 列表
lst = [1, 2, 3]
print(f"列表是可迭代对象: {hasattr(lst, '__iter__')}") # 输出: 列表是可迭代对象: True
# 元组
tup = (1, 2, 3)
print(f"元组是可迭代对象: {hasattr(tup, '__iter__')}") # 输出: 元组是可迭代对象: True
# 字符串
string = "hello"
print(f"字符串是可迭代对象: {hasattr(string, '__iter__')}") # 输出: 字符串是可迭代对象: True
# 字典
d = {"a": 1, "b": 2}
print(f"字典是可迭代对象: {hasattr(d, '__iter__')}") # 输出: 字典是可迭代对象: True
# 集合
s = {1, 2, 3}
print(f"集合是可迭代对象: {hasattr(s, '__iter__')}") # 输出: 集合是可迭代对象: True
# range 对象
r = range(5)
print(f"range 对象是可迭代对象: {hasattr(r, '__iter__')}") # 输出: range 对象是可迭代对象: True11.2.3 创建迭代器
示例:
python
class MyIterator:
"""自定义迭代器"""
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
"""返回迭代器本身"""
return self
def __next__(self):
"""返回下一个元素"""
if self.start < self.end:
value = self.start
self.start += 1
return value
else:
raise StopIteration
# 使用自定义迭代器
my_iter = MyIterator(1, 5)
for item in my_iter:
print(item) # 输出: 1, 2, 3, 4
# 再次使用迭代器(已经耗尽)
for item in my_iter:
print(item) # 不会输出任何内容
# 创建新的迭代器
my_iter2 = MyIterator(1, 5)
print(next(my_iter2)) # 输出: 1
print(next(my_iter2)) # 输出: 211.3 生成器
11.3.1 什么是生成器
生成器是一种特殊的迭代器,它使用 yield 语句来生成值。生成器可以节省内存,因为它不会一次性生成所有值,而是按需生成。
11.3.2 创建生成器
示例:
python
# 使用生成器表达式
gen1 = (x ** 2 for x in range(5))
print(type(gen1)) # 输出: <class 'generator'>
# 遍历生成器
for item in gen1:
print(item) # 输出: 0, 1, 4, 9, 16
# 使用生成器函数
def my_generator(n):
"""生成器函数"""
i = 0
while i < n:
yield i
i += 1
# 创建生成器
gen2 = my_generator(5)
print(type(gen2)) # 输出: <class 'generator'>
# 遍历生成器
for item in gen2:
print(item) # 输出: 0, 1, 2, 3, 4
# 生成器函数示例:斐波那契数列
def fibonacci(n):
"""生成斐波那契数列"""
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
# 使用生成器
for num in fibonacci(10):
print(num) # 输出: 0, 1, 1, 2, 3, 5, 8, 13, 21, 3411.3.3 send()
send() 方法用于向生成器发送值,并获取下一个值。
示例:
python
def echo_generator():
"""回显生成器"""
while True:
received = yield
print(f"Received: {received}")
# 创建生成器
gen = echo_generator()
# 启动生成器(第一次调用 next() 或 send(None))
next(gen) # 或 gen.send(None)
# 向生成器发送值
gen.send("Hello") # 输出: Received: Hello
gen.send("World") # 输出: Received: World
# 关闭生成器
gen.close()
# 示例:带初始值的生成器
def counter():
"""计数器生成器"""
count = 0
while True:
received = yield count
if received is not None:
count = received
else:
count += 1
# 创建生成器
gen = counter()
# 启动生成器
print(next(gen)) # 输出: 0
# 向生成器发送值
print(gen.send(10)) # 输出: 10
print(next(gen)) # 输出: 11
print(next(gen)) # 输出: 12
# 关闭生成器
gen.close()11.4 命名空间
11.4.1 什么是命名空间
命名空间是一个映射,将名称映射到对象。它用于避免命名冲突,不同命名空间中的同名名称不会相互影响。
11.4.2 三种命名空间
- 内置命名空间:包含 Python 内置的函数和变量,如
print()、len()等 - 全局命名空间:包含模块级别的变量和函数
- 局部命名空间:包含函数内部的变量和函数
示例:
python
# 全局命名空间
x = 10 # 全局变量
def func():
# 局部命名空间
y = 20 # 局部变量
print(f"局部变量 y: {y}")
print(f"全局变量 x: {x}")
# 调用函数
func() # 输出: 局部变量 y: 20, 全局变量 x: 10
# 访问全局变量
print(f"全局变量 x: {x}") # 输出: 全局变量 x: 10
# 尝试访问局部变量(会报错)
try:
print(f"局部变量 y: {y}")
except NameError as e:
print(f"错误: {e}") # 输出: 错误: name 'y' is not defined
# 内置命名空间
print(f"内置函数 len: {len}") # 输出: 内置函数 len: <built-in function len>
print(f"内置函数 print: {print}") # 输出: 内置函数 print: <built-in function print>11.5 作用域
11.5.1 什么是作用域
作用域是指变量的可见范围,即变量在哪些地方可以被访问。
11.5.2 四种作用域
- 内置作用域:包含内置函数和变量的作用域
- 全局作用域:模块级别的作用域
- 非局部作用域:嵌套函数中的外层函数作用域
- 局部作用域:函数内部的作用域
示例:
python
# 全局变量
x = 10
def outer():
# 非局部变量
y = 20
def inner():
# 局部变量
z = 30
print(f"局部变量 z: {z}")
print(f"非局部变量 y: {y}")
print(f"全局变量 x: {x}")
inner()
print(f"非局部变量 y: {y}")
print(f"全局变量 x: {x}")
# print(f"局部变量 z: {z}") # 会报错,因为 z 是 inner() 的局部变量
# 调用函数
outer()
# 输出:
# 局部变量 z: 30
# 非局部变量 y: 20
# 全局变量 x: 10
# 非局部变量 y: 20
# 全局变量 x: 10
# 访问全局变量
print(f"全局变量 x: {x}") # 输出: 全局变量 x: 10
# 使用 global 关键字修改全局变量
def modify_global():
global x
x = 100
print(f"修改后的全局变量 x: {x}")
modify_global() # 输出: 修改后的全局变量 x: 100
print(f"全局变量 x: {x}") # 输出: 全局变量 x: 100
# 使用 nonlocal 关键字修改非局部变量
def outer():
y = 20
def inner():
nonlocal y
y = 200
print(f"修改后的非局部变量 y: {y}")
inner()
print(f"非局部变量 y: {y}")
outer() # 输出: 修改后的非局部变量 y: 200, 非局部变量 y: 20011.6 闭包
11.6.1 什么是闭包
闭包是指一个函数能够访问其外部函数作用域中的变量,即使外部函数已经执行完毕。
11.6.2 使用闭包
示例:
python
def outer_function(x):
"""外部函数"""
def inner_function(y):
"""内部函数"""
return x + y
return inner_function
# 创建闭包
add_5 = outer_function(5)
add_10 = outer_function(10)
# 使用闭包
print(add_5(3)) # 输出: 8 (5 + 3)
print(add_10(3)) # 输出: 13 (10 + 3)
# 闭包示例:计数器
def make_counter():
"""创建计数器"""
count = 0
def counter():
"""计数器函数"""
nonlocal count
count += 1
return count
return counter
# 创建计数器
c1 = make_counter()
c2 = make_counter()
# 使用计数器
print(c1()) # 输出: 1
print(c1()) # 输出: 2
print(c1()) # 输出: 3
print(c2()) # 输出: 1(c2 是一个新的计数器)
print(c2()) # 输出: 211.6.3 查看闭包中的值
示例:
python
def outer_function(x):
"""外部函数"""
y = 10
def inner_function(z):
"""内部函数"""
return x + y + z
return inner_function
# 创建闭包
closure = outer_function(5)
# 查看闭包中的值
print(f"闭包的 __closure__ 属性: {closure.__closure__}")
# 遍历闭包中的单元格
for cell in closure.__closure__:
print(f"闭包中的值: {cell.cell_contents}")
# 使用闭包
print(f"closure(3) = {closure(3)}") # 输出: closure(3) = 18 (5 + 10 + 3)11.7 装饰器
11.7.1 什么是装饰器
装饰器是一种特殊的函数,它可以修改其他函数的行为。装饰器使用 @ 符号应用到函数上。
11.7.2 使用装饰器
示例:
python
def my_decorator(func):
"""装饰器函数"""
def wrapper(*args, **kwargs):
"""包装函数"""
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
# 使用装饰器
@my_decorator
def greet(name):
"""问候函数"""
print(f"Hello, {name}!")
return f"Greeted {name}"
# 调用函数
result = greet("Alice")
print(f"Result: {result}")
# 输出:
# Before function call
# Hello, Alice!
# After function call
# Result: Greeted Alice
# 装饰器示例:计时装饰器
import time
def timer_decorator(func):
"""计时装饰器"""
def wrapper(*args, **kwargs):
"""包装函数"""
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds")
return result
return wrapper
@timer_decorator
def slow_function():
"""慢函数"""
time.sleep(1)
print("Slow function executed")
# 调用函数
slow_function()
# 输出:
# Slow function executed
# Function slow_function took 1.0012 seconds11.7.3 多层装饰器
示例:
python
def decorator1(func):
"""装饰器1"""
def wrapper(*args, **kwargs):
print("Decorator 1: Before")
result = func(*args, **kwargs)
print("Decorator 1: After")
return result
return wrapper
def decorator2(func):
"""装饰器2"""
def wrapper(*args, **kwargs):
print("Decorator 2: Before")
result = func(*args, **kwargs)
print("Decorator 2: After")
return result
return wrapper
# 使用多层装饰器
@decorator1
@decorator2
def greet(name):
"""问候函数"""
print(f"Hello, {name}!")
return f"Greeted {name}"
# 调用函数
result = greet("Alice")
# 输出:
# Decorator 1: Before
# Decorator 2: Before
# Hello, Alice!
# Decorator 2: After
# Decorator 1: After11.7.4 带参数的装饰器
示例:
python
def repeat(n):
"""带参数的装饰器"""
def decorator(func):
"""装饰器函数"""
def wrapper(*args, **kwargs):
"""包装函数"""
for i in range(n):
print(f"Call {i+1}")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# 使用带参数的装饰器
@repeat(3)
def greet(name):
"""问候函数"""
print(f"Hello, {name}!")
return f"Greeted {name}"
# 调用函数
result = greet("Alice")
# 输出:
# Call 1
# Hello, Alice!
# Call 2
# Hello, Alice!
# Call 3
# Hello, Alice!
# 装饰器示例:带参数的日志装饰器
def log(level):
"""带参数的日志装饰器"""
def decorator(func):
"""装饰器函数"""
def wrapper(*args, **kwargs):
"""包装函数"""
print(f"[{level.upper()}] Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"[{level.upper()}] {func.__name__} returned {result}")
return result
return wrapper
return decorator
@log("info")
def add(a, b):
"""加法函数"""
return a + b
# 调用函数
result = add(5, 3)
# 输出:
# [INFO] Calling add
# [INFO] add returned 811.7.5 类装饰器
示例:
python
class CountCalls:
"""类装饰器:统计函数调用次数"""
def __init__(self, func):
"""初始化"""
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
"""调用方法"""
self.count += 1
print(f"Function {self.func.__name__} has been called {self.count} times")
return self.func(*args, **kwargs)
# 使用类装饰器
@CountCalls
def greet(name):
"""问候函数"""
print(f"Hello, {name}!")
return f"Greeted {name}"
# 调用函数
greet("Alice")
greet("Bob")
greet("Charlie")
# 输出:
# Function greet has been called 1 times
# Hello, Alice!
# Function greet has been called 2 times
# Hello, Bob!
# Function greet has been called 3 times
# Hello, Charlie!
# 类装饰器示例:带参数的类装饰器
class Repeat:
"""类装饰器:重复执行函数"""
def __init__(self, times):
"""初始化"""
self.times = times
def __call__(self, func):
"""调用方法"""
def wrapper(*args, **kwargs):
"""包装函数"""
for i in range(self.times):
result = func(*args, **kwargs)
return result
return wrapper
# 使用带参数的类装饰器
@Repeat(2)
def greet(name):
"""问候函数"""
print(f"Hello, {name}!")
return f"Greeted {name}"
# 调用函数
greet("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!第 12 章 进程与线程
进程与线程是操作系统中用于实现并发的两种基本单位。进程是资源分配的基本单位,线程是CPU调度的基本单位。Python提供了多种库来支持进程和线程的创建与管理。
12.1 并发与并行
12.1.1 并发
并发是指多个任务在同一时间段内交替执行,通过时间片轮转等方式,给人一种同时执行的错觉。
示例:
python
import time
# 并发执行示例
def task1():
"""任务1"""
for i in range(3):
print(f"Task 1: {i}")
time.sleep(1)
def task2():
"""任务2"""
for i in range(3):
print(f"Task 2: {i}")
time.sleep(1)
# 顺序执行
task1()
task2()
# 输出:
# Task 1: 0
# Task 1: 1
# Task 1: 2
# Task 2: 0
# Task 2: 1
# Task 2: 212.1.2 并行
并行是指多个任务在同一时刻同时执行,需要多个CPU核心支持。
示例:
python
import time
import multiprocessing
# 并行执行示例
def task1():
"""任务1"""
for i in range(3):
print(f"Task 1: {i}")
time.sleep(1)
def task2():
"""任务2"""
for i in range(3):
print(f"Task 2: {i}")
time.sleep(1)
# 并行执行
if __name__ == "__main__":
p1 = multiprocessing.Process(target=task1)
p2 = multiprocessing.Process(target=task2)
p1.start()
p2.start()
p1.join()
p2.join()
# 输出(可能的顺序):
# Task 1: 0
# Task 2: 0
# Task 1: 1
# Task 2: 1
# Task 1: 2
# Task 2: 212.2 多进程
12.2.1 什么是进程
进程是程序的一次执行过程,是操作系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源。
12.2.2 使用multiprocessing.Process创建进程
示例:
python
import multiprocessing
import time
def worker(name, delay):
"""工作函数"""
print(f"Worker {name} started")
time.sleep(delay)
print(f"Worker {name} finished")
if __name__ == "__main__":
# 创建进程
p1 = multiprocessing.Process(target=worker, args=('A', 2))
p2 = multiprocessing.Process(target=worker, args=('B', 1))
# 启动进程
p1.start()
p2.start()
print("Main process waiting for workers")
# 等待进程结束
p1.join()
p2.join()
print("Main process finished")
# 输出:
# Main process waiting for workers
# Worker A started
# Worker B started
# Worker B finished
# Worker A finished
# Main process finished12.2.3 自定义Process子类创建进程
示例:
python
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
"""自定义进程类"""
def __init__(self, name, delay):
super().__init__()
self.name = name
self.delay = delay
def run(self):
"""进程运行方法"""
print(f"Worker {self.name} started")
time.sleep(self.delay)
print(f"Worker {self.name} finished")
if __name__ == "__main__":
# 创建进程
p1 = MyProcess('A', 2)
p2 = MyProcess('B', 1)
# 启动进程
p1.start()
p2.start()
print("Main process waiting for workers")
# 等待进程结束
p1.join()
p2.join()
print("Main process finished")
# 输出:
# Main process waiting for workers
# Worker A started
# Worker B started
# Worker B finished
# Worker A finished
# Main process finished12.2.4 进程池
进程池可以管理多个进程,避免频繁创建和销毁进程的开销。
示例:
python
import multiprocessing
import time
def worker(num):
"""工作函数"""
print(f"Worker {num} started")
time.sleep(1)
print(f"Worker {num} finished")
return num * 2
if __name__ == "__main__":
# 创建进程池,最多4个进程
with multiprocessing.Pool(processes=4) as pool:
# 方法1:map
results = pool.map(worker, range(10))
print(f"Results (map): {results}")
# 方法2:apply_async
results = []
for i in range(10):
result = pool.apply_async(worker, (i,))
results.append(result)
# 获取结果
output = [r.get() for r in results]
print(f"Results (apply_async): {output}")
# 输出:
# Worker 0 started
# Worker 1 started
# Worker 2 started
# Worker 3 started
# Worker 0 finished
# Worker 4 started
# Worker 1 finished
# Worker 5 started
# Worker 2 finished
# Worker 6 started
# Worker 3 finished
# Worker 7 started
# Worker 4 finished
# Worker 8 started
# Worker 5 finished
# Worker 9 started
# Worker 6 finished
# Worker 7 finished
# Worker 8 finished
# Worker 9 finished
# Results (map): [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Results (apply_async): [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]12.2.5 进程间通信
进程间通信(IPC)是指不同进程之间交换数据的机制。Python提供了多种IPC方式,如管道、队列、共享内存等。
示例:
python
import multiprocessing
import time
# 1. 使用队列进行通信
def producer(queue):
"""生产者"""
for i in range(5):
item = f"Item {i}"
queue.put(item)
print(f"Produced: {item}")
time.sleep(0.5)
def consumer(queue):
"""消费者"""
while True:
item = queue.get()
if item is None: # 结束信号
break
print(f"Consumed: {item}")
time.sleep(1)
if __name__ == "__main__":
# 创建队列
queue = multiprocessing.Queue()
# 创建进程
p1 = multiprocessing.Process(target=producer, args=(queue,))
p2 = multiprocessing.Process(target=consumer, args=(queue,))
# 启动进程
p1.start()
p2.start()
# 等待生产者完成
p1.join()
# 发送结束信号
queue.put(None)
# 等待消费者完成
p2.join()
print("All processes finished")
# 输出:
# Produced: Item 0
# Consumed: Item 0
# Produced: Item 1
# Produced: Item 2
# Consumed: Item 1
# Produced: Item 3
# Produced: Item 4
# Consumed: Item 2
# Consumed: Item 3
# Consumed: Item 4
# All processes finished
# 2. 使用共享内存进行通信
import multiprocessing
def update_shared_value(shared_value):
"""更新共享值"""
for i in range(5):
shared_value.value += 1
print(f"Updated shared value: {shared_value.value}")
if __name__ == "__main__":
# 创建共享值
shared_value = multiprocessing.Value('i', 0) # 'i'表示整数类型
# 创建进程
p1 = multiprocessing.Process(target=update_shared_value, args=(shared_value,))
p2 = multiprocessing.Process(target=update_shared_value, args=(shared_value,))
# 启动进程
p1.start()
p2.start()
# 等待进程结束
p1.join()
p2.join()
print(f"Final shared value: {shared_value.value}")
# 输出(可能的顺序):
# Updated shared value: 1
# Updated shared value: 2
# Updated shared value: 3
# Updated shared value: 4
# Updated shared value: 5
# Updated shared value: 6
# Updated shared value: 7
# Updated shared value: 8
# Updated shared value: 9
# Updated shared value: 10
# Final shared value: 1012.3 多线程
12.3.1 什么是线程
线程是进程内的一个执行单元,是CPU调度的基本单位。一个进程可以包含多个线程,线程共享进程的内存空间和系统资源。
12.3.2 使用threading.Thread创建线程
示例:
python
import threading
import time
def worker(name, delay):
"""工作函数"""
print(f"Worker {name} started")
time.sleep(delay)
print(f"Worker {name} finished")
if __name__ == "__main__":
# 创建线程
t1 = threading.Thread(target=worker, args=('A', 2))
t2 = threading.Thread(target=worker, args=('B', 1))
# 启动线程
t1.start()
t2.start()
print("Main thread waiting for workers")
# 等待线程结束
t1.join()
t2.join()
print("Main thread finished")
# 输出:
# Main thread waiting for workers
# Worker A started
# Worker B started
# Worker B finished
# Worker A finished
# Main thread finished12.3.3 自定义Thread子类创建线程
示例:
python
import threading
import time
class MyThread(threading.Thread):
"""自定义线程类"""
def __init__(self, name, delay):
super().__init__()
self.name = name
self.delay = delay
def run(self):
"""线程运行方法"""
print(f"Worker {self.name} started")
time.sleep(self.delay)
print(f"Worker {self.name} finished")
if __name__ == "__main__":
# 创建线程
t1 = MyThread('A', 2)
t2 = MyThread('B', 1)
# 启动线程
t1.start()
t2.start()
print("Main thread waiting for workers")
# 等待线程结束
t1.join()
t2.join()
print("Main thread finished")
# 输出:
# Main thread waiting for workers
# Worker A started
# Worker B started
# Worker B finished
# Worker A finished
# Main thread finished12.3.4 线程池
线程池可以管理多个线程,避免频繁创建和销毁线程的开销。
示例:
python
import concurrent.futures
import time
def worker(num):
"""工作函数"""
print(f"Worker {num} started")
time.sleep(1)
print(f"Worker {num} finished")
return num * 2
if __name__ == "__main__":
# 创建线程池,最多4个线程
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
# 方法1:map
results = list(executor.map(worker, range(10)))
print(f"Results (map): {results}")
# 方法2:submit
futures = []
for i in range(10):
future = executor.submit(worker, i)
futures.append(future)
# 获取结果
results = [f.result() for f in concurrent.futures.as_completed(futures)]
print(f"Results (submit): {results}")
# 输出:
# Worker 0 started
# Worker 1 started
# Worker 2 started
# Worker 3 started
# Worker 0 finished
# Worker 4 started
# Worker 1 finished
# Worker 5 started
# Worker 2 finished
# Worker 6 started
# Worker 3 finished
# Worker 7 started
# Worker 4 finished
# Worker 8 started
# Worker 5 finished
# Worker 9 started
# Worker 6 finished
# Worker 7 finished
# Worker 8 finished
# Worker 9 finished
# Results (map): [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Results (submit): [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]12.3.5 互斥锁
互斥锁用于保护共享资源,防止多个线程同时访问导致的数据竞争问题。
示例:
python
import threading
import time
# 共享资源
counter = 0
# 创建互斥锁
lock = threading.Lock()
def increment():
"""增加计数器"""
global counter
for _ in range(100000):
# 获取锁
with lock:
counter += 1
if __name__ == "__main__":
# 创建线程
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
# 启动线程
t1.start()
t2.start()
# 等待线程结束
t1.join()
t2.join()
print(f"Final counter value: {counter}")
# 输出:
# Final counter value: 200000
# 没有互斥锁的情况
def increment_no_lock():
"""增加计数器(无锁)"""
global counter
for _ in range(100000):
counter += 1
if __name__ == "__main__":
counter = 0
# 创建线程
t1 = threading.Thread(target=increment_no_lock)
t2 = threading.Thread(target=increment_no_lock)
# 启动线程
t1.start()
t2.start()
# 等待线程结束
t1.join()
t2.join()
print(f"Final counter value (no lock): {counter}")
# 输出(可能小于200000):
# Final counter value (no lock): 14567812.3.6 GIL
GIL(Global Interpreter Lock)是Python解释器中的一个机制,它确保同一时刻只有一个线程在执行Python字节码。这意味着在CPython中,多线程并不能充分利用多核CPU。
示例:
python
import threading
import time
import multiprocessing
def cpu_bound_task():
"""CPU密集型任务"""
result = 0
for i in range(10**8):
result += i
return result
# 使用多线程
start_time = time.time()
threads = []
for _ in range(4):
t = threading.Thread(target=cpu_bound_task)
threads.append(t)
t.start()
for t in threads:
t.join()
thread_time = time.time() - start_time
print(f"Multi-threading time: {thread_time:.2f} seconds")
# 使用多进程
start_time = time.time()
processes = []
for _ in range(4):
p = multiprocessing.Process(target=cpu_bound_task)
processes.append(p)
p.start()
for p in processes:
p.join()
process_time = time.time() - start_time
print(f"Multi-processing time: {process_time:.2f} seconds")
# 输出(多进程通常更快):
# Multi-threading time: 45.23 seconds
# Multi-processing time: 11.34 seconds总结:
- IO密集型任务:多线程通常比多进程更高效,因为线程切换成本低
- CPU密集型任务:多进程通常比多线程更高效,因为可以充分利用多核CPU
- GIL的影响:在CPython中,多线程不能充分利用多核CPU,对于CPU密集型任务,建议使用多进程
第 13 章 网络编程
网络编程是指编写程序通过网络进行通信的技术。Python提供了丰富的库来支持网络编程,如socket、urllib、requests等。
13.1 套接字编程
套接字(Socket)是网络通信的基本单位,它提供了一种机制,使得不同计算机上的进程可以进行通信。
13.1.1 TCP套接字
TCP(Transmission Control Protocol)是一种可靠的、面向连接的协议,它提供了错误检测和重传机制,确保数据的可靠传输。
示例:TCP服务器
python
import socket
# 创建TCP服务器
def tcp_server():
# 创建套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_socket.bind(('127.0.0.1', 8888))
# 监听连接
server_socket.listen(5)
print("TCP服务器已启动,等待连接...")
while True:
# 接受连接
client_socket, client_address = server_socket.accept()
print(f"接受到来自 {client_address} 的连接")
# 接收数据
data = client_socket.recv(1024)
print(f"接收到数据: {data.decode('utf-8')}")
# 发送数据
response = "Hello, client!"
client_socket.send(response.encode('utf-8'))
# 关闭连接
client_socket.close()
if __name__ == "__main__":
tcp_server()示例:TCP客户端
python
import socket
# 创建TCP客户端
def tcp_client():
# 创建套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
client_socket.connect(('127.0.0.1', 8888))
print("已连接到服务器")
# 发送数据
message = "Hello, server!"
client_socket.send(message.encode('utf-8'))
print(f"已发送数据: {message}")
# 接收数据
data = client_socket.recv(1024)
print(f"接收到数据: {data.decode('utf-8')}")
# 关闭连接
client_socket.close()
if __name__ == "__main__":
tcp_client()13.1.2 UDP套接字
UDP(User Datagram Protocol)是一种无连接的协议,它不提供错误检测和重传机制,但是传输速度更快。
示例:UDP服务器
python
import socket
# 创建UDP服务器
def udp_server():
# 创建套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
server_socket.bind(('127.0.0.1', 8888))
print("UDP服务器已启动,等待数据...")
while True:
# 接收数据
data, client_address = server_socket.recvfrom(1024)
print(f"接受到来自 {client_address} 的数据: {data.decode('utf-8')}")
# 发送数据
response = "Hello, client!"
server_socket.sendto(response.encode('utf-8'), client_address)
if __name__ == "__main__":
udp_server()示例:UDP客户端
python
import socket
# 创建UDP客户端
def udp_client():
# 创建套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送数据
message = "Hello, server!"
client_socket.sendto(message.encode('utf-8'), ('127.0.0.1', 8888))
print(f"已发送数据: {message}")
# 接收数据
data, server_address = client_socket.recvfrom(1024)
print(f"接收到来自 {server_address} 的数据: {data.decode('utf-8')}")
# 关闭连接
client_socket.close()
if __name__ == "__main__":
udp_client()13.2 HTTP客户端
HTTP(Hypertext Transfer Protocol)是一种用于分布式、协作式和超媒体信息系统的应用层协议。Python提供了多种库来发送HTTP请求,如urllib、requests等。
13.2.1 使用urllib
示例:
python
import urllib.request
import urllib.parse
# 发送GET请求
def get_request():
url = "https://www.example.com"
response = urllib.request.urlopen(url)
data = response.read()
print(f"状态码: {response.getcode()}")
print(f"响应数据: {data.decode('utf-8')}")
# 发送POST请求
def post_request():
url = "https://httpbin.org/post"
data = urllib.parse.urlencode({'name': 'Alice', 'age': 18}).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
response = urllib.request.urlopen(req)
data = response.read()
print(f"状态码: {response.getcode()}")
print(f"响应数据: {data.decode('utf-8')}")
if __name__ == "__main__":
print("发送GET请求:")
get_request()
print("\n发送POST请求:")
post_request()13.2.2 使用requests
**requests**是一个更简洁、更强大的HTTP客户端库,需要使用pip install requests安装。
示例:
python
import requests
# 发送GET请求
def get_request():
url = "https://www.example.com"
response = requests.get(url)
print(f"状态码: {response.status_code}")
print(f"响应数据: {response.text}")
# 发送POST请求
def post_request():
url = "https://httpbin.org/post"
data = {'name': 'Alice', 'age': 18}
response = requests.post(url, data=data)
print(f"状态码: {response.status_code}")
print(f"响应数据: {response.json()}")
# 发送带参数的GET请求
def get_request_with_params():
url = "https://httpbin.org/get"
params = {'name': 'Alice', 'age': 18}
response = requests.get(url, params=params)
print(f"状态码: {response.status_code}")
print(f"响应数据: {response.json()}")
# 发送带headers的请求
def get_request_with_headers():
url = "https://httpbin.org/get"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
response = requests.get(url, headers=headers)
print(f"状态码: {response.status_code}")
print(f"响应数据: {response.json()}")
if __name__ == "__main__":
print("发送GET请求:")
get_request()
print("\n发送POST请求:")
post_request()
print("\n发送带参数的GET请求:")
get_request_with_params()
print("\n发送带headers的请求:")
get_request_with_headers()13.3 HTTP服务器
Python提供了多种方式来创建HTTP服务器,如http.server、Flask、Django等。
13.3.1 使用http.server
示例:
python
import http.server
import socketserver
# 创建HTTP服务器
def http_server():
PORT = 8000
# 创建处理器
Handler = http.server.SimpleHTTPRequestHandler
# 创建服务器
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"HTTP服务器已启动,监听端口 {PORT}")
# 启动服务器
httpd.serve_forever()
if __name__ == "__main__":
http_server()13.3.2 使用Flask
**Flask**是一个轻量级的Web框架,需要使用pip install flask安装。
示例:
python
from flask import Flask, request, jsonify
# 创建Flask应用
app = Flask(__name__)
# 定义路由
@app.route('/')
def index():
return 'Hello, World!'
@app.route('/api', methods=['GET'])
def api_get():
name = request.args.get('name', 'World')
return f'Hello, {name}!'
@app.route('/api', methods=['POST'])
def api_post():
data = request.json
if not data:
return jsonify({'error': 'No data provided'}), 400
name = data.get('name', 'World')
age = data.get('age', 0)
return jsonify({'message': f'Hello, {name}!', 'age': age})
if __name__ == "__main__":
app.run(debug=True, port=5000)13.4 网络爬虫
网络爬虫是一种自动获取网页内容的程序。Python提供了多种库来支持网络爬虫,如requests、beautifulsoup4、scrapy等。
示例:使用requests和beautifulsoup4爬取网页
python
import requests
from bs4 import BeautifulSoup
# 爬取网页
def crawl_website():
url = "https://www.example.com"
response = requests.get(url)
if response.status_code == 200:
# 解析网页
soup = BeautifulSoup(response.text, 'html.parser')
# 获取标题
title = soup.title.string
print(f"网页标题: {title}")
# 获取所有链接
links = soup.find_all('a')
print("\n网页链接:")
for link in links:
href = link.get('href')
text = link.text.strip()
print(f"{text}: {href}")
else:
print(f"爬取失败,状态码: {response.status_code}")
if __name__ == "__main__":
crawl_website()第 14 章 正则表达式
正则表达式是一种用于匹配字符串中字符组合的模式。Python提供了re模块来支持正则表达式操作。
14.1 正则表达式基础
14.1.1 正则表达式语法
| 元字符 | 描述 | 示例 | | ------- | ------------------------------------------------ | ---------------------------------------------- | --- | ------------------ | | . | 匹配任意字符(除了换行符) | a.b 匹配 "acb", "a1b" 等 | | ^ | 匹配字符串的开始 | ^abc 匹配以 "abc" 开头的字符串 | | $ | 匹配字符串的结束 | abc$ 匹配以 "abc" 结尾的字符串 | | * | 匹配前面的字符零次或多次 | ab*c 匹配 "ac", "abc", "abbc" 等 | | + | 匹配前面的字符一次或多次 | ab+c 匹配 "abc", "abbc" 等,但不匹配 "ac" | | ? | 匹配前面的字符零次或一次 | ab?c 匹配 "ac", "abc",但不匹配 "abbc" | | {n} | 匹配前面的字符恰好n次 | ab{2}c 匹配 "abbc" | | {n,} | 匹配前面的字符至少n次 | ab{2,}c 匹配 "abbc", "abbbc" 等 | | {n,m} | 匹配前面的字符至少n次,最多m次 | ab{2,3}c 匹配 "abbc", "abbbc" | | [] | 匹配方括号内的任意一个字符 | [abc] 匹配 "a", "b", "c" | | [^] | 匹配除了方括号内的任意一个字符 | [^abc] 匹配除了 "a", "b", "c" 以外的任意字符 | | \d | 匹配数字,等价于 [0-9] | \d+ 匹配一个或多个数字 | | \D | 匹配非数字,等价于 [^0-9] | \D+ 匹配一个或多个非数字字符 | | \w | 匹配字母、数字、下划线,等价于 [a-zA-Z0-9_] | \w+ 匹配一个或多个字母、数字、下划线 | | \W | 匹配非字母、数字、下划线,等价于 [^a-zA-Z0-9_] | \W+ 匹配一个或多个非字母、数字、下划线字符 | | \s | 匹配空白字符,包括空格、制表符、换行符等 | \s+ 匹配一个或多个空白字符 | | \S | 匹配非空白字符 | \S+ 匹配一个或多个非空白字符 | | \b | 匹配单词边界 | \bword\b 匹配完整的单词 "word" | | \B | 匹配非单词边界 | \Bword\B 匹配单词内部的 "word" | | | | 匹配左右任意一个表达式 | a | b 匹配 "a" 或 "b" | | () | 分组,将括号内的表达式作为一个整体 | (ab)+ 匹配 "ab", "abab" 等 |
14.1.2 常用正则表达式函数
re.match():从字符串的开始位置匹配正则表达式。
示例:
python
import re
# 匹配字符串开头
pattern = r'^Hello'
result = re.match(pattern, 'Hello, World!')
if result:
print(f"匹配成功: {result.group()}") # 输出: 匹配成功: Hello
else:
print("匹配失败")
# 匹配失败的情况
result = re.match(pattern, 'Hi, Hello!')
if result:
print(f"匹配成功: {result.group()}")
else:
print("匹配失败") # 输出: 匹配失败re.search():在整个字符串中搜索正则表达式。
示例:
python
import re
# 搜索字符串
pattern = r'Hello'
result = re.search(pattern, 'Hi, Hello!')
if result:
print(f"匹配成功: {result.group()}") # 输出: 匹配成功: Hello
else:
print("匹配失败")
# 搜索失败的情况
result = re.search(pattern, 'Hi, World!')
if result:
print(f"匹配成功: {result.group()}")
else:
print("匹配失败") # 输出: 匹配失败re.findall():查找字符串中所有匹配正则表达式的部分,返回一个列表。
示例:
python
import re
# 查找所有数字
pattern = r'\d+'
result = re.findall(pattern, 'There are 123 apples and 456 bananas.')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['123', '456']
# 查找所有单词
pattern = r'\w+'
result = re.findall(pattern, 'Hello, World!')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['Hello', 'World']re.sub():替换字符串中匹配正则表达式的部分。
示例:
python
import re
# 替换数字为 'X'
pattern = r'\d+'
result = re.sub(pattern, 'X', 'There are 123 apples and 456 bananas.')
print(f"替换结果: {result}") # 输出: 替换结果: There are X apples and X bananas.
# 替换空格为 '-'
pattern = r'\s+'
result = re.sub(pattern, '-', 'Hello World!')
print(f"替换结果: {result}") # 输出: 替换结果: Hello-World!re.split():根据正则表达式分割字符串。
示例:
python
import re
# 根据空格分割字符串
pattern = r'\s+'
result = re.split(pattern, 'Hello World!')
print(f"分割结果: {result}") # 输出: 分割结果: ['Hello', 'World!']
# 根据逗号或分号分割字符串
pattern = r'[,;]'
result = re.split(pattern, 'apple,banana;orange')
print(f"分割结果: {result}") # 输出: 分割结果: ['apple', 'banana', 'orange']14.2 正则表达式高级应用
14.2.1 分组
示例:
python
import re
# 提取日期
pattern = r'(\d{4})-(\d{2})-(\d{2})'
result = re.search(pattern, 'Today is 2024-01-01.')
if result:
print(f"完整匹配: {result.group()}") # 输出: 完整匹配: 2024-01-01
print(f"年份: {result.group(1)}") # 输出: 年份: 2024
print(f"月份: {result.group(2)}") # 输出: 月份: 01
print(f"日期: {result.group(3)}") # 输出: 日期: 01
# 命名分组
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
result = re.search(pattern, 'Today is 2024-01-01.')
if result:
print(f"年份: {result.group('year')}") # 输出: 年份: 2024
print(f"月份: {result.group('month')}") # 输出: 月份: 01
print(f"日期: {result.group('day')}") # 输出: 日期: 0114.2.2 贪婪与非贪婪匹配
贪婪匹配:默认情况下,正则表达式会尽可能多地匹配字符。
非贪婪匹配:在量词后添加 ? 可以实现非贪婪匹配,即尽可能少地匹配字符。
示例:
python
import re
# 贪婪匹配
pattern = r'<.*>'
result = re.search(pattern, '<div>Hello</div>')
if result:
print(f"贪婪匹配: {result.group()}") # 输出: 贪婪匹配: <div>Hello</div>
# 非贪婪匹配
pattern = r'<.*?>'
result = re.search(pattern, '<div>Hello</div>')
if result:
print(f"非贪婪匹配: {result.group()}") # 输出: 非贪婪匹配: <div>
# 提取HTML标签内容
pattern = r'<(\w+)>(.*?)</\1>'
result = re.search(pattern, '<div>Hello</div>')
if result:
print(f"标签名: {result.group(1)}") # 输出: 标签名: div
print(f"标签内容: {result.group(2)}") # 输出: 标签内容: Hello14.2.3 零宽断言
零宽断言是一种特殊的正则表达式语法,它匹配位置而不是字符。
- 正向先行断言:
(?=pattern)匹配后面跟着 pattern 的位置 - 负向先行断言:
(?!pattern)匹配后面不跟着 pattern 的位置 - 正向后行断言:
(?<=pattern)匹配前面是 pattern 的位置 - 负向后行断言:
(?<!pattern)匹配前面不是 pattern 的位置
示例:
python
import re
# 正向先行断言:匹配后面跟着 "@example.com" 的用户名
pattern = r'\w+(?=@example.com)'
result = re.findall(pattern, 'user1@example.com user2@gmail.com user3@example.com')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['user1', 'user3']
# 负向先行断言:匹配后面不跟着 "@example.com" 的用户名
pattern = r'\w+(?!@example.com)'
result = re.findall(pattern, 'user1@example.com user2@gmail.com user3@example.com')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['user2']
# 正向后行断言:匹配前面是 "http://" 或 "https://" 的URL
pattern = r'(?<=https?:\/\/)\w+\.\w+'
result = re.findall(pattern, 'Visit http://example.com and https://google.com')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['example.com', 'google.com']
# 负向后行断言:匹配前面不是 "http://" 或 "https://" 的URL
pattern = r'(?<!https?:\/\/)\w+\.\w+'
result = re.findall(pattern, 'Visit example.com and https://google.com')
print(f"匹配结果: {result}") # 输出: 匹配结果: ['example.com']14.2.4 常见正则表达式示例
示例:验证邮箱地址
python
import re
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
# 测试
emails = [
'user@example.com',
'user.name@example.com',
'user_name@example.com',
'user+tag@example.com',
'user@sub.example.com',
'user@example', # 无效
'user@.com', # 无效
'@example.com' # 无效
]
for email in emails:
print(f"{email}: {'有效' if validate_email(email) else '无效'}")示例:验证手机号码
python
import re
def validate_phone(phone):
pattern = r'^1[3-9]\d{9}$'
return bool(re.match(pattern, phone))
# 测试
phones = [
'13812345678',
'15912345678',
'18912345678',
'13012345678',
'12345678901', # 无效
'1381234567', # 无效
'138123456789' # 无效
]
for phone in phones:
print(f"{phone}: {'有效' if validate_phone(phone) else '无效'}")示例:验证URL
python
import re
def validate_url(url):
pattern = r'^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$'
return bool(re.match(pattern, url))
# 测试
urls = [
'https://www.example.com',
'http://example.com',
'https://example.com/path',
'https://example.com/path?query=123',
'example.com', # 无效
'https://', # 无效
'https://.com' # 无效
]
for url in urls:
print(f"{url}: {'有效' if validate_url(url) else '无效'}")示例:提取HTML标签
python
import re
def extract_tags(html):
pattern = r'<([a-zA-Z][a-zA-Z0-9]*)([^>]*)>(.*?)</\1>'
return re.findall(pattern, html, re.DOTALL)
# 测试
html = '<div class="container">Hello <b>World</b>!</div>'
tags = extract_tags(html)
for tag, attributes, content in tags:
print(f"标签: {tag}")
print(f"属性: {attributes}")
print(f"内容: {content}")
print()14.3 正则表达式最佳实践
使用原始字符串:在Python中,正则表达式模式最好使用原始字符串(以
r开头),这样可以避免反斜杠的转义问题。编译正则表达式:对于频繁使用的正则表达式,使用
re.compile()编译可以提高性能。
示例:
python
import re
# 编译正则表达式
pattern = re.compile(r'\d+')
# 使用编译后的正则表达式
result1 = pattern.findall('There are 123 apples.')
result2 = pattern.findall('There are 456 bananas.')
print(f"结果1: {result1}") # 输出: 结果1: ['123']
print(f"结果2: {result2}") # 输出: 结果2: ['456']使用非贪婪匹配:当匹配可能包含多个重复的内容时,使用非贪婪匹配(
*?,+?,??)可以避免过度匹配。使用分组:使用分组可以提取匹配的部分,使代码更加清晰。
使用命名分组:使用命名分组(
(?P<name>pattern))可以使代码更加可读性。测试正则表达式:使用在线工具(如https://regex101.com/)测试正则表达式,确保其正确性。
注意性能:复杂的正则表达式可能会影响性能,特别是在处理大量数据时。尽量使用简单、高效的正则表达式。