Skip to content

Python

导航目录

第 1 章 基础知识

1.1 注释

Python 中的注释用于解释代码的功能和逻辑,提高代码可读性。Python 支持两种注释方式:

  • 单行注释:使用 # 符号,从 # 开始到行尾的内容均为注释。

    python
    # 这是一行单行注释
    print("Hello, World!")  # 行尾注释
  • 多行注释:使用三引号(单引号或双引号)包裹多行内容,通常用于函数或类的文档说明

    python
    """
    这是一个多行注释
    可以跨越多行
    通常用于函数或类的文档说明
    """
    def example():
        """这是函数的文档字符串"""
        pass

1.2 变量与常量

变量是存储数据的容器,Python 是动态类型语言,无需显式声明变量类型,变量类型会根据赋值自动推导。

  • 变量命名规则

    • 变量名只能包含字母、数字和下划线(_)。
    • 变量名不能以数字开头。
    • 变量名区分大小写(nameName 是不同变量)。
    • 避免使用 Python 关键字(如 ifforwhile 等)作为变量名。
  • 变量赋值

    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 数据类型分类

  • 按可变性分类
    • 不可变数据类型:一旦创建,值不能修改,包括 intfloatcomplexstrbooltuple。(数值\字符串\元组)
    • 可变数据类型:创建后可以修改其值,包括 listdictset

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-50
    • 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):由字符组成的序列,使用单引号、双引号或三引号包裹。

    python
    s1 = 'Hello'  # 单引号字符串
    s2 = "World"  # 双引号字符串
    s3 = '''Multi-line
    string'''  # 三引号多行字符串
    
    # 字符串操作示例
    print(s1 + " " + s2)  # 字符串拼接
    print(s1 * 3)  # 字符串重复
    print(len(s1))  # 字符串长度
  • 布尔值bool):只有 TrueFalse 两个值,用于逻辑判断。

  • 列表list):有序、可变的元素集合,使用方括号 [] 定义。

    python
    fruits = ["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):有序、不可变的元素集合,使用圆括号 () 定义。

    python
    coordinates = (10, 20)
    print(f"元组: {coordinates}")
    print(f"第一个元素: {coordinates[0]}")
    # coordinates[0] = 30  # 尝试修改元组会引发错误
  • 字典dict):键值对的无序集合,使用大括号 {} 定义。

    python
    person = {"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() 函数定义。

    python
    unique_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))  # True

1.3.3 小整数池和大整数池

Python 为了提高性能,对于一些常用的整数,会提前创建对象并缓存起来,这就是小整数池。

  • 小整数池:Python 会缓存 -5256 之间的整数,这些整数在内存中只有一个实例。

    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:在同一代码块中定义

    python
    num1 = 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 World
1.4.1.2 格式化输出

Python 提供了多种格式化输出的方法:

  1. 使用 % 占位符
python
num1 = 10
num2 = 3.14
# 格式化字符串
str1 = "num1 = %d, num2 = %.3f" % (num1, num2)
print(str1)  # 输出: num1 = 10, num2 = 3.140
  1. 使用 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
  1. 数字格式化
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
  1. 使用大括号 {} 转义
python
# 显示大括号
print("{} 对应的位置是 {{0}}".format("hello"))  # 输出: hello 对应的位置是 {0}
  1. 使用 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.14

1.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 支持多种运算符

  • 算术运算符+(加)、-(减)、*(乘)、/(除)、//(整除)、%(取余)、**(幂)。

    python
    print(10 + 5)  # 15
    print(10 / 3)  # 3.333...
    print(10 // 3)  # 3
    print(10 % 3)  # 1
    print(2 ** 3)  # 8
  • 比较运算符==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。

    python
    a = 10
    b = 5
    print(a == b)  # False
    print(a > b)   # True
    print(a <= b)  # False
  • 逻辑运算符and(与)、or(或)、not(非)。

    python
    x = True
    y = False
    print(x and y)  # False
    print(x or y)   # True
    print(not x)    # False
  • 赋值运算符=+=-=*=/= 等。

    python
    num = 10
    num += 5  # 等同于 num = num + 5
    print(num)  # 15
    
    num *= 2  # 等同于 num = num * 2
    print(num)  # 30
  • 成员运算符in(存在)、not in(不存在)。

    python
    fruits = ["apple", "banana", "cherry"]
    print("apple" in fruits)  # True
    print("orange" not in fruits)  # True
  • 身份运算符is(相同对象)、is not(不同对象)。

    python
    a = [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)为模块、函数、类添加说明。
    python
    def 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 提供了 whilefor 两种循环语句。

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)  # 输出: True

3.2.8 获取列表长度

python
fruits = ["apple", "banana", "cherry"]

# 使用 len() 函数
length = len(fruits)
print(length)  # 输出: 3

3.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.6

3.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)  # 输出: ababababab

3.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)使用前缀 rR,可以避免转义字符的特殊处理。

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"))  # 会引发 ValueError

3.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))  # 输出: -0042

3.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)  # 输出: True

3.4.6 获取元组长度

python
fruits = ("apple", "banana", "cherry")

# 使用 len() 函数
length = len(fruits)
print(length)  # 输出: 3

3.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.6

3.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 30

3.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)  # 输出: True

3.5.5 获取集合长度

python
fruits = {"apple", "banana", "cherry"}

# 使用 len() 函数
length = len(fruits)
print(length)  # 输出: 3

3.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.6

3.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)  # 输出: 3

3.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"])  # 输出: Alice

3.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 + b

4.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: 20

4.4 使用函数的好处

  1. 代码重用:避免重复编写相同的代码
  2. 模块化:将复杂的问题分解为更小的、可管理的部分
  3. 可维护性:更容易理解和修改代码
  4. 可读性:函数名可以清晰地表达代码的功能
  5. 可测试性:更容易对代码进行测试

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))  # 输出: 15

4.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 York

4.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 York

4.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)  # 输出: None

4.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 = 20

4.9 变量的作用域

变量的作用域是指变量在程序中可以被访问的范围。Python 中有四种作用域:

  1. 局部作用域:在函数内部定义的变量,只能在函数内部访问
  2. 嵌套作用域:在嵌套函数中,内层函数可以访问外层函数的变量
  3. 全局作用域:在模块级别定义的变量,可以在模块的任何地方访问
  4. 内置作用域: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 global

4.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))  # 输出: 55

4.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 或 False

5.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 School

6.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 Camry

6.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 School

6.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: 2

6.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))  # 输出: 24

6.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 method

6.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"))  # 输出: False

6.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 sapiens

6.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"))  # 输出: False

6.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: 1300

7.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 str

9.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: 200

9.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文件不存在错误
IOErrorI/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.14159

10.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.53975

10.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 导入模块时的搜索顺序:

  1. 当前目录
  2. 环境变量 PYTHONPATH 中的目录
  3. Python 标准库目录
  4. 任何 .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 * 只会导入 PIgreetPerson

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 的包:

  1. 创建 mypackage 目录
  2. mypackage 目录中创建 __init__.py 文件
  3. 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 1

10.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 1

10.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 2

10.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操作系统接口
sysPython 解释器相关
math数学函数
random随机数生成
datetime日期和时间处理
jsonJSON 数据处理
re正则表达式
collections集合数据类型
itertools迭代器工具
functools函数工具
time时间相关函数
threading线程相关
multiprocessing进程相关
socket网络套接字
httpHTTP 相关

示例:

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"])  # 输出: Alice

10.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 requests

10.9.2 pycharm 中导入

在 PyCharm 中导入第三方库:

  1. 打开 PyCharm 项目
  2. 点击 File -> Settings -> Project: <项目名> -> Python Interpreter
  3. 点击 + 按钮,搜索并安装需要的库
  4. 点击 Install Package 完成安装

10.10 打包自己的库并安装

步骤:

  1. 创建项目目录结构
  2. 编写 setup.py 文件
  3. 构建包
  4. 安装包

示例:

目录结构:

myproject/
├── mypackage/
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
└── setup.py

setup.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 对象是可迭代对象: True

11.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))  # 输出: 2

11.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, 34

11.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 三种命名空间

  1. 内置命名空间:包含 Python 内置的函数和变量,如 print()len()
  2. 全局命名空间:包含模块级别的变量和函数
  3. 局部命名空间:包含函数内部的变量和函数

示例:

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 四种作用域

  1. 内置作用域:包含内置函数和变量的作用域
  2. 全局作用域:模块级别的作用域
  3. 非局部作用域:嵌套函数中的外层函数作用域
  4. 局部作用域:函数内部的作用域

示例:

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: 200

11.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())  # 输出: 2

11.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 seconds

11.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: After

11.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 8

11.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: 2

12.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: 2

12.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 finished

12.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 finished

12.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: 10

12.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 finished

12.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 finished

12.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): 145678

12.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提供了丰富的库来支持网络编程,如socketurllibrequests等。

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请求,如urllibrequests等。

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.serverFlaskDjango等。

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提供了多种库来支持网络爬虫,如requestsbeautifulsoup4scrapy等。

示例:使用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')}")  # 输出: 日期: 01

14.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)}")  # 输出: 标签内容: Hello

14.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 正则表达式最佳实践

  1. 使用原始字符串:在Python中,正则表达式模式最好使用原始字符串(以r开头),这样可以避免反斜杠的转义问题。

  2. 编译正则表达式:对于频繁使用的正则表达式,使用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']
  1. 使用非贪婪匹配:当匹配可能包含多个重复的内容时,使用非贪婪匹配(*?, +?, ??)可以避免过度匹配。

  2. 使用分组:使用分组可以提取匹配的部分,使代码更加清晰。

  3. 使用命名分组:使用命名分组((?P<name>pattern))可以使代码更加可读性。

  4. 测试正则表达式:使用在线工具(如https://regex101.com/)测试正则表达式,确保其正确性。

  5. 注意性能:复杂的正则表达式可能会影响性能,特别是在处理大量数据时。尽量使用简单、高效的正则表达式。