Python入门完全教程

教程简介

从零开始系统学习Python编程语言的完整教程,涵盖环境搭建、变量与数据类型、控制流、函数、面向对象编程、文件操作、异常处理、常用标准库等内容,包含丰富的代码示例和实战项目,适合零基础入门到进阶。

Python 入门完全教程

从零开始,系统学习 Python 编程语言


目录


第一章:Python 简介与环境搭建

1.1 Python 是什么

Python 是一门由 Guido van Rossum 于 1991 年创建的高级编程语言。它以简洁优雅的语法著称,强调代码的可读性。Python 支持多种编程范式,包括面向对象编程、函数式编程和过程式编程。

Python 的核心哲学可以总结为:

  • 优雅优于丑陋
  • 明确优于隐晦
  • 简单优于复杂
  • 可读性很重要

1.2 Python 的应用领域

Python 被广泛应用于以下领域:

  1. Web 开发:Django、Flask、FastAPI 等框架
  2. 数据科学与机器学习:NumPy、Pandas、Scikit-learn、TensorFlow、PyTorch
  3. 自动化运维:Ansible、SaltStack
  4. 网络爬虫:Scrapy、BeautifulSoup、Selenium
  5. 桌面应用:Tkinter、PyQt
  6. 游戏开发:Pygame
  7. 科学计算:SciPy、SymPy

1.3 安装 Python

Windows 安装

  1. 访问官网 https://www.python.org/downloads/
  2. 下载最新版本的安装包
  3. 运行安装程序,务必勾选 "Add Python to PATH"
  4. 点击 "Install Now" 完成安装

macOS 安装

# 使用 Homebrew 安装
brew install python3

# 或者从官网下载安装包

Linux 安装

# Ubuntu/Debian
sudo apt update
sudo apt install python3 python3-pip

# CentOS/RHEL
sudo yum install python3

# Arch Linux
sudo pacman -S python python-pip

1.4 验证安装

# 检查 Python 版本
python3 --version

# 检查 pip 版本
pip3 --version

1.5 选择开发工具

推荐以下开发工具:

  • VS Code:轻量级,插件丰富,适合初学者
  • PyCharm:专业 Python IDE,功能强大
  • Jupyter Notebook:适合数据分析和学习
  • IDLE:Python 自带的简单编辑器

1.6 第一个 Python 程序

创建文件 hello.py

# 这是我的第一个 Python 程序
# 文件名:hello.py

# 使用 print() 函数输出文本
print("Hello, World!")
print("欢迎来到 Python 的世界!")

# 使用 input() 函数获取用户输入
name = input("请输入你的名字:")
print(f"你好,{name}!很高兴认识你!")

运行程序:

python3 hello.py

1.7 Python 交互模式

# 进入 Python 交互模式
python3

# 在交互模式中尝试
>>> 2 + 3
5
>>> print("Hello")
Hello
>>> exit()  # 退出交互模式

1.8 注释

# 这是单行注释

"""
这是多行注释
可以写很多行
通常用于文档说明
'''

'''
这也是多行注释
使用三个单引号
效果与三个双引号相同
'''

# 在实际开发中,建议使用以下方式写文档字符串
def greet(name):
    """
    向用户打招呼。
    
    参数:
        name (str): 用户的名字
    返回:
        str: 打招呼的消息
    """
    return f"Hello, {name}!"

第二章:变量与数据类型

2.1 变量

变量是存储数据的容器。Python 中的变量不需要声明类型,解释器会根据赋值自动推断。

# 变量命名规则
# 1. 只能包含字母、数字和下划线
# 2. 不能以数字开头
# 3. 不能使用 Python 关键字
# 4. 区分大小写

# 合法的变量名
name = "Alice"
age = 25
student_name = "Bob"
_private_var = "private"
MAX_VALUE = 100

# 不合法的变量名(会报错)
# 2name = "error"      # 不能以数字开头
# my-name = "error"    # 不能包含连字符
# class = "error"      # 不能使用关键字

# Python 命名约定
# - 变量名和函数名使用小写字母加下划线(snake_case)
# - 常量使用全大写字母
# - 类名使用驼峰命名法(CamelCase)

# 动态类型特性
x = 10          # x 是整数类型
print(type(x))  # <class 'int'>

x = "hello"     # 现在 x 变成了字符串类型
print(type(x))  # <class 'str'>

x = 3.14        # 现在 x 变成了浮点数类型
print(type(x))  # <class 'float'>

2.2 数字类型

# 整数 (int)
a = 10
b = -5
c = 0
big_number = 1_000_000  # 下划线分隔,提高可读性
print(big_number)  # 1000000

# 不同进制的整数
binary = 0b1010     # 二进制,等于 10
octal = 0o17        # 八进制,等于 15
hexadecimal = 0xFF  # 十六进制,等于 255
print(binary, octal, hexadecimal)  # 10 15 255

# 浮点数 (float)
pi = 3.14159
temperature = -20.5
scientific = 1.5e-4  # 科学计数法,等于 0.00015
print(scientific)  # 0.00015

# 浮点数精度问题
print(0.1 + 0.2)  # 0.30000000000000004
# 解决方案:使用 decimal 模块
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))  # 0.3

# 复数 (complex)
z = 3 + 4j
print(z.real)  # 3.0
print(z.imag)  # 4.0
print(abs(z))  # 5.0 (模)

# 布尔值 (bool)
is_student = True
is_graduated = False
print(type(is_student))  # <class 'bool'>

# 布尔值是整数的子类
print(True + True)   # 2
print(True * 10)     # 10
print(False + 1)     # 1

2.3 字符串类型

# 字符串创建方式
s1 = 'Hello'        # 单引号
s2 = "World"        # 双引号
s3 = '''多行
字符串'''            # 三单引号
s4 = """多行
字符串"""            # 三双引号

# 包含引号的字符串
s5 = "It's a dog"   # 包含单引号
s6 = 'He said "hi"' # 包含双引号
s7 = "He said \"hi\""  # 使用转义字符

# 常用转义字符
print("换行符:第一行\n第二行")
print("制表符:列1\t列2")
print("反斜杠:\\")
print("引号:\"")

# 原始字符串(忽略转义)
print(r"C:\new\folder")  # 输出:C:\new\folder

# 字符串是不可变的
s = "Hello"
# s[0] = 'h'  # TypeError: 'str' object does not support item assignment

# 字符串索引
s = "Python"
print(s[0])    # P(第一个字符)
print(s[-1])   # n(最后一个字符)
print(s[2])    # t

# 字符串切片
s = "Hello, Python!"
print(s[0:5])    # Hello(从索引0到4)
print(s[7:])     # Python!(从索引7到末尾)
print(s[:5])     # Hello(从开头到索引4)
print(s[-7:-1])  # Python
print(s[::2])    # Hlo yhn(每隔一个字符)
print(s[::-1])   # !nohtyP ,olleH(反转字符串)

# 字符串方法
text = "  Hello, Python World!  "
print(text.strip())          # 去除两端空白
print(text.lstrip())         # 去除左端空白
print(text.rstrip())         # 去除右端空白
print(text.lower())          # 转换为小写
print(text.upper())          # 转换为大写
print(text.title())          # 首字母大写
print(text.capitalize())     # 首字母大写
print(text.swapcase())       # 大小写互换
print(text.find("Python"))   # 查找子串位置
print(text.replace("Python", "Java"))  # 替换子串
print(text.split(","))       # 分割字符串
print(text.count("l"))       # 统计子串出现次数
print(text.startswith(" "))  # 检查开头
print(text.endswith("!"))    # 检查结尾
print(text.isdigit())        # 检查是否全为数字
print(text.isalpha())        # 检查是否全为字母

# 字符串格式化
name = "Alice"
age = 25

# 方法1:f-string(推荐,Python 3.6+)
msg1 = f"我叫{name},今年{age}岁"
print(msg1)

# 方法2:format() 方法
msg2 = "我叫{},今年{}岁".format(name, age)
print(msg2)

# 方法3:% 格式化(旧式)
msg3 = "我叫%s,今年%d岁" % (name, age)
print(msg3)

# f-string 高级用法
import math
print(f"圆周率是 {math.pi:.2f}")  # 保留两位小数
print(f"二进制:{42:b}")  # 二进制表示
print(f"十六进制:{255:x}")  # 十六进制表示
print(f"科学计数法:{1234567:e}")  # 科学计数法
print(f"百分比:{0.85:.1%}")  # 百分比
print(f"右对齐:{'hello':>20}")  # 右对齐
print(f"左对齐:{'hello':<20}")  # 左对齐
print(f"居中:{'hello':^20}")  # 居中
print(f"填充:{'hello':*^20}")  # 用*填充居中

2.4 类型转换

# 隐式类型转换
a = 10      # int
b = 3.14    # float
c = a + b   # 自动转换为 float
print(c, type(c))  # 13.14 <class 'float'>

# 显式类型转换
# int() 转换为整数
print(int(3.9))      # 3(截断,不是四舍五入)
print(int("123"))    # 123
print(int(True))     # 1
print(int(False))    # 0

# float() 转换为浮点数
print(float(10))     # 10.0
print(float("3.14")) # 3.14

# str() 转换为字符串
print(str(123))      # "123"
print(str(3.14))     # "3.14"
print(str(True))     # "True"

# bool() 转换为布尔值
# 以下值被视为 False
print(bool(0))       # False
print(bool(0.0))     # False
print(bool(""))      # False
print(bool([]))      # False
print(bool(None))    # False

# 其他值被视为 True
print(bool(1))       # True
print(bool("hello")) # True
print(bool([1, 2]))  # True

# 类型检查
x = 42
print(type(x))              # <class 'int'>
print(isinstance(x, int))   # True
print(isinstance(x, float)) # False

第三章:运算符与表达式

3.1 算术运算符

# 基本算术运算符
a = 15
b = 4

print(a + b)   # 加法:19
print(a - b)   # 减法:11
print(a * b)   # 乘法:60
print(a / b)   # 除法:3.75(结果为浮点数)
print(a // b)  # 整除:3(向下取整)
print(a % b)   # 取余:3
print(a ** b)  # 幂运算:50625(15的4次方)

# 注意负数的整除
print(-7 // 2)   # -4(向下取整)
print(-7 % 2)    # 1(余数为正)

# 增量赋值运算符
x = 10
x += 5   # 等价于 x = x + 5,结果 15
x -= 3   # 等价于 x = x - 3,结果 12
x *= 2   # 等价于 x = x * 2,结果 24
x /= 4   # 等价于 x = x / 4,结果 6.0
x //= 2  # 等价于 x = x // 2,结果 3.0
x **= 3  # 等价于 x = x ** 3,结果 27.0

3.2 比较运算符

# 比较运算符返回布尔值
a = 10
b = 20

print(a == b)   # 等于:False
print(a != b)   # 不等于:True
print(a > b)    # 大于:False
print(a < b)    # 小于:True
print(a >= b)   # 大于等于:False
print(a <= b)   # 小于等于:True

# 链式比较
x = 5
print(1 < x < 10)      # True
print(1 < x < 3)       # False
print(1 < x > 3 < 10)  # True

# 字符串比较(按字典序)
print("apple" < "banana")   # True
print("abc" < "abd")        # True
print("abc" < "ab")         # False

3.3 逻辑运算符

# and、or、not
x = True
y = False

print(x and y)  # False(两个都为True才为True)
print(x or y)   # True(至少一个为True就为True)
print(not x)    # False(取反)

# 短路求值
# and:第一个为False,不计算第二个
# or:第一个为True,不计算第二个

# 实际应用
age = 25
income = 5000
if age >= 18 and income >= 3000:
    print("符合贷款条件")

# 逻辑运算符返回的是操作数,不一定是布尔值
print(0 or "hello")     # "hello"
print("hello" and "world")  # "world"
print(0 and "hello")    # 0
print("" or "default")  # "default"

3.4 位运算符

# 位运算符
a = 0b1100  # 12
b = 0b1010  # 10

print(bin(a & b))   # 按位与:0b1000 (8)
print(bin(a | b))   # 按位或:0b1110 (14)
print(bin(a ^ b))   # 按位异或:0b0110 (6)
print(bin(~a))       # 按位取反:-0b1101 (-13)
print(bin(a << 2))   # 左移:0b110000 (48)
print(bin(a >> 2))   # 右移:0b11 (3)

# 实际应用:权限系统
READ = 0b100    # 4
WRITE = 0b010   # 2
EXECUTE = 0b001 # 1

# 设置权限
permission = READ | WRITE  # 读写权限
print(bin(permission))  # 0b110

# 检查权限
has_read = bool(permission & READ)
has_execute = bool(permission & EXECUTE)
print(f"有读权限:{has_read}")      # True
print(f"有执行权限:{has_execute}")  # False

3.5 成员运算符和身份运算符

# 成员运算符:in, not in
fruits = ["apple", "banana", "cherry"]
print("apple" in fruits)      # True
print("grape" not in fruits)  # True

text = "Hello, Python"
print("Python" in text)       # True

# 身份运算符:is, is not
# is 比较的是内存地址(id),== 比较的是值
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)   # True(值相同)
print(a is b)   # False(不是同一个对象)
print(a is c)   # True(是同一个对象)
print(id(a), id(b), id(c))

# 小整数池(-5 到 256)和字符串驻留
x = 256
y = 256
print(x is y)   # True(小整数池)

x = 257
y = 257
print(x is y)   # False(超出小整数池,但可能因实现而异)

# None 的比较
x = None
print(x is None)      # 推荐写法
print(x == None)      # 也可以,但不推荐

3.6 运算符优先级

# 优先级从高到低:
# 1. **(幂运算)
# 2. +x, -x, ~x(一元运算符)
# 3. *, /, //, %
# 4. +, -
# 5. <<, >>
# 6. &
# 7. ^
# 8. |
# 9. ==, !=, <, <=, >, >=, is, in
# 10. not
# 11. and
# 12. or

# 示例
result = 2 + 3 * 4 ** 2
# 先算 4**2 = 16,再算 3*16 = 48,最后 2+48 = 50
print(result)  # 50

# 使用括号改变优先级
result = (2 + 3) * 4 ** 2
print(result)  # 80

第四章:控制流语句

4.1 条件语句

# if 语句
age = 18
if age >= 18:
    print("你已经成年了")

# if-else 语句
age = 15
if age >= 18:
    print("成年人")
else:
    print("未成年人")

# if-elif-else 语句
score = 85
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"
print(f"成绩等级:{grade}")

# 三元表达式(条件表达式)
age = 20
status = "成年" if age >= 18 else "未成年"
print(status)

# 嵌套条件
num = 15
if num > 0:
    if num % 2 == 0:
        print("正偶数")
    else:
        print("正奇数")
elif num < 0:
    print("负数")
else:
    print("零")

# match-case(Python 3.10+)
command = "start"
match command:
    case "start":
        print("启动程序")
    case "stop":
        print("停止程序")
    case "restart":
        print("重启程序")
    case _:
        print("未知命令")

4.2 for 循环

# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(f"我喜欢{fruit}")

# 使用 range()
# range(stop) - 从0到stop-1
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

# range(start, stop) - 从start到stop-1
for i in range(2, 6):
    print(i)  # 2, 3, 4, 5

# range(start, stop, step)
for i in range(0, 10, 2):
    print(i)  # 0, 2, 4, 6, 8

# 倒序
for i in range(10, 0, -1):
    print(i)  # 10, 9, 8, ..., 1

# enumerate() - 同时获取索引和值
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")

# enumerate 设置起始索引
for index, fruit in enumerate(fruits, start=1):
    print(f"{index}: {fruit}")

# zip() - 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
    print(f"{name} 今年 {age} 岁")

# 遍历字典
person = {"name": "Alice", "age": 25, "city": "Beijing"}

# 遍历键
for key in person:
    print(key)

# 遍历值
for value in person.values():
    print(value)

# 遍历键值对
for key, value in person.items():
    print(f"{key}: {value}")

# 列表推导式
squares = [x ** 2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件的列表推导式
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# 嵌套列表推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 字典推导式
squares_dict = {x: x ** 2 for x in range(5)}
print(squares_dict)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 集合推导式
unique_lengths = {len(word) for word in ["hello", "world", "hi", "hey"]}
print(unique_lengths)  # {2, 3, 5}

4.3 while 循环

# 基本 while 循环
count = 0
while count < 5:
    print(count)
    count += 1

# 用户输入验证
while True:
    user_input = input("请输入一个正整数:")
    if user_input.isdigit() and int(user_input) > 0:
        number = int(user_input)
        break
    print("输入无效,请重试")
print(f"你输入的是:{number}")

# while-else
count = 0
while count < 5:
    print(count)
    count += 1
else:
    print("循环正常结束")

4.4 循环控制

# break - 跳出循环
for i in range(10):
    if i == 5:
        break
    print(i)  # 0, 1, 2, 3, 4

# continue - 跳过当前迭代
for i in range(10):
    if i % 2 == 0:
        continue
    print(i)  # 1, 3, 5, 7, 9

# pass - 占位符
for i in range(10):
    if i % 2 == 0:
        pass  # 暂时什么都不做,以后再实现
    else:
        print(i)

# 嵌套循环与标签(Python没有goto,用函数模拟)
def search_in_matrix(matrix, target):
    for i, row in enumerate(matrix):
        for j, val in enumerate(row):
            if val == target:
                return i, j
    return -1, -1

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(search_in_matrix(matrix, 5))  # (1, 1)

4.5 综合练习

# 练习1:九九乘法表
for i in range(1, 10):
    for j in range(1, i + 1):
        print(f"{j}×{i}={i*j}", end="\t")
    print()

# 练习2:判断素数
def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

# 打印100以内的素数
primes = [n for n in range(2, 100) if is_prime(n)]
print(primes)

# 练习3:猜数字游戏
import random

secret = random.randint(1, 100)
attempts = 0

print("欢迎来到猜数字游戏!")
print("我已经想好了一个1到100之间的数字。")

while True:
    guess = int(input("请输入你的猜测:"))
    attempts += 1
    
    if guess < secret:
        print("太小了!")
    elif guess > secret:
        print("太大了!")
    else:
        print(f"恭喜你!猜对了!答案就是 {secret}")
        print(f"你一共猜了 {attempts} 次")
        break

第五章:函数与模块

5.1 函数定义与调用

# 基本函数定义
def greet():
    print("Hello, World!")

greet()  # 调用函数

# 带参数的函数
def greet_name(name):
    print(f"Hello, {name}!")

greet_name("Alice")

# 带返回值的函数
def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 8

# 多个返回值
def get_dimensions():
    return 1920, 1080

width, height = get_dimensions()
print(f"分辨率:{width}x{height}")

# 默认参数
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice")           # Hello, Alice!
greet("Bob", "Hi")       # Hi, Bob!

# 可变参数 *args
def sum_all(*args):
    total = 0
    for num in args:
        total += num
    return total

print(sum_all(1, 2, 3))       # 6
print(sum_all(1, 2, 3, 4, 5)) # 15

# 关键字可变参数 **kwargs
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=25, city="Beijing")

# 混合使用
def func(a, b, *args, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"kwargs={kwargs}")

func(1, 2, 3, 4, x=5, y=6)

# keyword-only 参数
def func(a, b, *, c, d):
    print(a, b, c, d)

func(1, 2, c=3, d=4)  # 正确
# func(1, 2, 3, 4)    # 错误:c 和 d 必须用关键字传递

5.2 作用域

# 全局变量和局部变量
x = 10  # 全局变量

def func():
    x = 20  # 局部变量
    print(f"函数内:{x}")

func()     # 函数内:20
print(f"函数外:{x}")  # 函数外:10

# global 关键字
x = 10

def func():
    global x
    x = 20
    print(f"函数内:{x}")

func()     # 函数内:20
print(f"函数外:{x}")  # 函数外:20

# nonlocal 关键字
def outer():
    x = 10
    def inner():
        nonlocal x
        x = 20
    inner()
    print(x)  # 20

outer()

# LEGB 规则
# L - Local(局部作用域)
# E - Enclosing(嵌套函数的外层作用域)
# G - Global(全局作用域)
# B - Built-in(内置作用域)

5.3 Lambda 表达式

# 基本 lambda
square = lambda x: x ** 2
print(square(5))  # 25

# 多参数
add = lambda x, y: x + y
print(add(3, 5))  # 8

# 在排序中使用
students = [
    {"name": "Alice", "grade": 88},
    {"name": "Bob", "grade": 95},
    {"name": "Charlie", "grade": 72}
]

# 按成绩排序
students.sort(key=lambda s: s["grade"])
print(students)

# 在 map() 中使用
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# 在 filter() 中使用
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even)  # [2, 4]

# 在 reduce() 中使用
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120

5.4 装饰器

# 简单装饰器
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后

# 带参数的装饰器
def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

# 保留原函数信息的装饰器
import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper 的文档字符串"""
        print("装饰器执行")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """example 的文档字符串"""
    pass

print(example.__name__)  # example
print(example.__doc__)   # example 的文档字符串

# 实用装饰器:计时器
import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行耗时:{end - start:.4f}秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "完成"

result = slow_function()

5.5 模块与包

# 创建模块
# 文件:math_utils.py
def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

PI = 3.14159

# 使用模块
import math_utils
print(math_utils.add(3, 5))

# 从模块导入特定内容
from math_utils import add, PI
print(add(3, 5))
print(PI)

# 导入并重命名
import math_utils as mu
print(mu.add(3, 5))

# 导入所有(不推荐)
from math_utils import *

# 包的结构
# mypackage/
#     __init__.py
#     module1.py
#     module2.py
#     subpackage/
#         __init__.py
#         module3.py

# 常用标准库模块
import os          # 操作系统相关
import sys         # 系统相关
import datetime    # 日期时间
import json        # JSON 处理
import re          # 正则表达式
import math        # 数学函数
import random      # 随机数
import collections # 高级数据结构
import itertools   # 迭代器工具
import functools   # 函数工具
import pathlib     # 路径处理

# 模块搜索路径
import sys
print(sys.path)

# 安装第三方模块
# pip install requests
# pip install numpy
# pip install flask

5.6 递归函数

# 阶乘
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))  # 120

# 斐波那契数列
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 带缓存的斐波那契
from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(50))  # 12586269025

# 二分查找(递归)
def binary_search(arr, target, low=0, high=None):
    if high is None:
        high = len(arr) - 1
    
    if low > high:
        return -1
    
    mid = (low + high) // 2
    
    if arr[mid] == target:
        return mid
    elif arr[mid] < target:
        return binary_search(arr, target, mid + 1, high)
    else:
        return binary_search(arr, target, low, mid - 1)

numbers = [1, 3, 5, 7, 9, 11, 13, 15]
print(binary_search(numbers, 7))  # 3

第六章:数据结构

6.1 列表(List)

# 创建列表
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2, 3]]
empty = []

# 访问元素
print(fruits[0])    # apple
print(fruits[-1])   # cherry

# 修改元素
fruits[0] = "grape"
print(fruits)  # ['grape', 'banana', 'cherry']

# 列表方法
fruits = ["apple", "banana", "cherry"]

# 添加元素
fruits.append("date")          # 末尾添加
fruits.insert(1, "blueberry")  # 指定位置插入
fruits.extend(["elderberry", "fig"])  # 扩展列表

# 删除元素
fruits.remove("banana")  # 删除指定值
popped = fruits.pop()    # 弹出末尾元素
popped = fruits.pop(0)   # 弹出指定位置元素
del fruits[0]            # 删除指定位置元素
fruits.clear()           # 清空列表

# 查找元素
fruits = ["apple", "banana", "cherry", "apple"]
print(fruits.index("apple"))    # 0(第一次出现的位置)
print(fruits.count("apple"))    # 2(出现次数)
print("banana" in fruits)       # True

# 排序
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort()              # 原地排序
print(numbers)              # [1, 1, 2, 3, 4, 5, 6, 9]

numbers.sort(reverse=True)  # 降序排序
print(numbers)              # [9, 6, 5, 4, 3, 2, 1, 1]

# sorted() 不修改原列表
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(numbers)         # [3, 1, 4, 1, 5]
print(sorted_numbers)  # [1, 1, 3, 4, 5]

# 反转
numbers = [1, 2, 3, 4, 5]
numbers.reverse()      # 原地反转
print(numbers)         # [5, 4, 3, 2, 1]

reversed_numbers = list(reversed(numbers))  # 不修改原列表

# 列表复制
original = [1, 2, 3, [4, 5]]
shallow = original.copy()      # 浅拷贝
shallow = original[:]          # 浅拷贝
import copy
deep = copy.deepcopy(original) # 深拷贝

original[3].append(6)
print(original)  # [1, 2, 3, [4, 5, 6]]
print(shallow)   # [1, 2, 3, [4, 5, 6]]  浅拷贝受影响
print(deep)      # [1, 2, 3, [4, 5]]     深拷贝不受影响

6.2 元组(Tuple)

# 创建元组
coordinates = (10, 20)
single = (42,)  # 单元素元组需要逗号
empty = ()
mixed = (1, "hello", 3.14)

# 元组是不可变的
# coordinates[0] = 15  # TypeError

# 元组操作
print(coordinates[0])     # 10
print(len(coordinates))   # 2
print(10 in coordinates)  # True

# 元组解包
x, y = coordinates
print(x, y)  # 10 20

# 交换变量
a, b = 1, 2
a, b = b, a
print(a, b)  # 2 1

# 元组方法
t = (1, 2, 3, 2, 2)
print(t.count(2))   # 3
print(t.index(3))   # 2

# 命名元组
from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y)  # 10 20
print(p[0], p[1])  # 10 20

6.3 字典(Dictionary)

# 创建字典
person = {
    "name": "Alice",
    "age": 25,
    "city": "Beijing"
}

# 也可以使用 dict() 构造函数
person = dict(name="Alice", age=25, city="Beijing")

# 访问元素
print(person["name"])           # Alice
print(person.get("email", "N/A"))  # N/A(默认值)

# 修改和添加
person["age"] = 26              # 修改
person["email"] = "a@b.com"    # 添加

# 删除
del person["email"]             # 删除键值对
email = person.pop("email", None)  # 弹出(带默认值)
person.popitem()                # 弹出最后一个键值对

# 字典方法
person = {"name": "Alice", "age": 25, "city": "Beijing"}

print(person.keys())    # dict_keys(['name', 'age', 'city'])
print(person.values())  # dict_values(['Alice', 25, 'Beijing'])
print(person.items())   # dict_items([('name', 'Alice'), ...])

# 更新字典
person.update({"age": 26, "email": "a@b.com"})

# 合并字典(Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2
print(merged)  # {'a': 1, 'b': 3, 'c': 4}

# 字典推导式
squares = {x: x**2 for x in range(6)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 嵌套字典
students = {
    "Alice": {"age": 20, "grade": 88},
    "Bob": {"age": 22, "grade": 95}
}
print(students["Alice"]["grade"])  # 88

# defaultdict
from collections import defaultdict

word_count = defaultdict(int)
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
    word_count[word] += 1
print(dict(word_count))  # {'apple': 3, 'banana': 2, 'cherry': 1}

# Counter
from collections import Counter

words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counter = Counter(words)
print(counter)           # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(counter.most_common(2))  # [('apple', 3), ('banana', 2)]

6.4 集合(Set)

# 创建集合
fruits = {"apple", "banana", "cherry"}
numbers = set([1, 2, 3, 2, 1])  # 自动去重
print(numbers)  # {1, 2, 3}

# 空集合(不能用 {},那是空字典)
empty_set = set()
empty_dict = {}

# 集合操作
fruits = {"apple", "banana", "cherry"}

# 添加和删除
fruits.add("date")
fruits.discard("banana")  # 删除(不存在不报错)
fruits.remove("cherry")   # 删除(不存在会报错)

# 集合运算
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

print(a | b)   # 并集:{1, 2, 3, 4, 5, 6, 7, 8}
print(a & b)   # 交集:{4, 5}
print(a - b)   # 差集:{1, 2, 3}
print(a ^ b)   # 对称差集:{1, 2, 3, 6, 7, 8}

# 集合推导式
squares = {x**2 for x in range(10)}
print(squares)  # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}

# frozenset(不可变集合)
frozen = frozenset([1, 2, 3])
# frozen.add(4)  # AttributeError

6.5 数据结构选择指南

# 何时使用列表:有序、可重复、需要索引访问
scores = [88, 92, 75, 92, 88]

# 何时使用元组:不可变数据、函数返回值、字典键
point = (10, 20)
rgb_color = (255, 128, 0)

# 何时使用字典:键值对映射、快速查找
user = {"id": 1, "name": "Alice", "email": "a@b.com"}

# 何时使用集合:去重、集合运算、成员测试
unique_visitors = {"user1", "user2", "user3"}

# 性能比较
# 列表查找:O(n)
# 字典查找:O(1)
# 集合查找:O(1)

第七章:面向对象编程

7.1 类与对象

# 定义类
class Dog:
    # 类变量(所有实例共享)
    species = "Canis familiaris"
    
    # 初始化方法(构造函数)
    def __init__(self, name, age):
        # 实例变量
        self.name = name
        self.age = age
    
    # 实例方法
    def bark(self):
        return f"{self.name} says: Woof!"
    
    def get_info(self):
        return f"{self.name} is {self.age} years old"

# 创建对象
dog1 = Dog("Buddy", 3)
dog2 = Dog("Charlie", 5)

print(dog1.bark())      # Buddy says: Woof!
print(dog2.get_info())  # Charlie is 5 years old
print(Dog.species)      # Canis familiaris

# 动态添加属性
dog1.color = "Brown"
print(dog1.color)  # Brown

7.2 访问控制

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner        # 公有属性
        self._type = "savings"    # 保护属性(约定)
        self.__balance = balance  # 私有属性(名称修饰)
    
    # getter 方法
    @property
    def balance(self):
        return self.__balance
    
    # setter 方法
    @balance.setter
    def balance(self, value):
        if value < 0:
            raise ValueError("余额不能为负数")
        self.__balance = value
    
    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self.__balance += amount
    
    def withdraw(self, amount):
        if amount > self.__balance:
            raise ValueError("余额不足")
        self.__balance -= amount

# 使用
account = BankAccount("Alice", 1000)
print(account.balance)    # 1000(通过 property 访问)
account.deposit(500)
print(account.balance)    # 1500
account.withdraw(200)
print(account.balance)    # 1300

# account.__balance  # AttributeError(私有属性)
# 但可以通过 _BankAccount__balance 访问(不推荐)
print(account._BankAccount__balance)  # 1300

7.3 继承

# 基类
class Animal:
    def __init__(self, name, sound):
        self.name = name
        self.sound = sound
    
    def speak(self):
        return f"{self.name} says {self.sound}"
    
    def __str__(self):
        return f"{self.name} ({self.__class__.__name__})"

# 派生类
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name, "Woof")
    
    def fetch(self, item):
        return f"{self.name} fetches the {item}"

class Cat(Animal):
    def __init__(self, name):
        super().__init__(name, "Meow")
    
    def purr(self):
        return f"{self.name} purrs..."

# 使用
dog = Dog("Buddy")
cat = Cat("Whiskers")

print(dog.speak())   # Buddy says Woof
print(dog.fetch("ball"))  # Buddy fetches the ball
print(cat.speak())   # Whiskers says Meow
print(cat.purr())    # Whiskers purrs...

# isinstance 和 issubclass
print(isinstance(dog, Dog))      # True
print(isinstance(dog, Animal))   # True
print(issubclass(Dog, Animal))   # True

# 多继承
class Flyable:
    def fly(self):
        return f"{self.name} is flying"

class Swimmable:
    def swim(self):
        return f"{self.name} is swimming"

class Duck(Animal, Flyable, Swimmable):
    def __init__(self, name):
        super().__init__(name, "Quack")

duck = Duck("Donald")
print(duck.speak())  # Donald says Quack
print(duck.fly())    # Donald is flying
print(duck.swim())   # Donald is swimming

# MRO(方法解析顺序)
print(Duck.__mro__)

7.4 多态

# 多态示例
class Shape:
    def area(self):
        raise NotImplementedError
    
    def perimeter(self):
        raise NotImplementedError

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        import math
        return math.pi * self.radius ** 2
    
    def perimeter(self):
        import math
        return 2 * math.pi * self.radius

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

class Triangle(Shape):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    
    def area(self):
        s = (self.a + self.b + self.c) / 2
        return (s * (s - self.a) * (s - self.b) * (s - self.c)) ** 0.5
    
    def perimeter(self):
        return self.a + self.b + self.c

# 多态:同一接口,不同实现
shapes = [Circle(5), Rectangle(4, 6), Triangle(3, 4, 5)]

for shape in shapes:
    print(f"{shape.__class__.__name__}:")
    print(f"  面积: {shape.area():.2f}")
    print(f"  周长: {shape.perimeter():.2f}")

# 鸭子类型
class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm quacking like a duck!")

def make_it_quack(thing):
    thing.quack()

make_it_quack(Duck())    # Quack!
make_it_quack(Person())  # I'm quacking like a duck!

7.5 魔术方法

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
    
    def __repr__(self):
        return f"Vector({self.x!r}, {self.y!r})"
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    
    def __len__(self):
        return int((self.x**2 + self.y**2)**0.5)
    
    def __bool__(self):
        return self.x != 0 or self.y != 0
    
    def __getitem__(self, index):
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        raise IndexError("索引超出范围")

# 使用
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1)          # Vector(3, 4)
print(v1 + v2)     # Vector(4, 6)
print(v1 - v2)     # Vector(2, 2)
print(v1 * 3)      # Vector(9, 12)
print(v1 == v2)    # False
print(len(v1))     # 5
print(bool(v1))    # True
print(v1[0])       # 3

7.6 抽象基类

from abc import ABC, abstractmethod

class Database(ABC):
    @abstractmethod
    def connect(self):
        pass
    
    @abstractmethod
    def disconnect(self):
        pass
    
    @abstractmethod
    def query(self, sql):
        pass
    
    def execute(self, sql):
        """非抽象方法,有默认实现"""
        print(f"执行:{sql}")
        return self.query(sql)

class MySQL(Database):
    def __init__(self, host, port):
        self.host = host
        self.port = port
    
    def connect(self):
        print(f"连接到 MySQL {self.host}:{self.port}")
    
    def disconnect(self):
        print("断开 MySQL 连接")
    
    def query(self, sql):
        print(f"执行 MySQL 查询:{sql}")
        return []

# db = Database()  # TypeError: 不能实例化抽象类
db = MySQL("localhost", 3306)
db.connect()
db.execute("SELECT * FROM users")
db.disconnect()

7.7 类方法和静态方法

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    
    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"
    
    @classmethod
    from_string(cls, date_string):
        """类方法:从字符串创建 Date 对象"""
        year, month, day = map(int, date_string.split('-'))
        return cls(year, month, day)
    
    @staticmethod
    def is_valid_date(year, month, day):
        """静态方法:验证日期是否有效"""
        if month < 1 or month > 12:
            return False
        if day < 1 or day > 31:
            return False
        return True

# 使用
date1 = Date(2024, 1, 15)
date2 = Date.from_string("2024-03-20")

print(date1)  # 2024-01-15
print(date2)  # 2024-03-20

print(Date.is_valid_date(2024, 2, 29))  # True
print(Date.is_valid_date(2024, 13, 1))  # False

7.8 组合与聚合

# 组合:部分与整体共存亡
class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower
    
    def start(self):
        return f"引擎启动 ({self.horsepower}HP)"

class Car:
    def __init__(self, brand, horsepower):
        self.brand = brand
        self.engine = Engine(horsepower)  # 组合
    
    def start(self):
        return f"{self.brand}: {self.engine.start()}"

car = Car("Toyota", 150)
print(car.start())  # Toyota: 引擎启动 (150HP)

# 聚合:部分可以独立存在
class Team:
    def __init__(self, name):
        self.name = name
        self.members = []
    
    def add_member(self, member):
        self.members.append(member)
    
    def show_members(self):
        return [str(m) for m in self.members]

class Player:
    def __init__(self, name, number):
        self.name = name
        self.number = number
    
    def __str__(self):
        return f"#{self.number} {self.name}"

team = Team("Dream Team")
p1 = Player("Alice", 10)
p2 = Player("Bob", 7)

team.add_member(p1)
team.add_member(p2)
print(team.show_members())  # ['#10 Alice', '#7 Bob']

第八章:文件操作

8.1 文本文件操作

# 写入文件
# 使用 with 语句确保文件正确关闭
with open("example.txt", "w", encoding="utf-8") as f:
    f.write("Hello, World!\n")
    f.write("这是第二行\n")
    f.write("这是第三行\n")

# 读取文件
with open("example.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)

# 逐行读取
with open("example.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())  # strip() 去除换行符

# 读取所有行到列表
with open("example.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
    print(lines)  # ['Hello, World!\n', '这是第二行\n', '这是第三行\n']

# 追加内容
with open("example.txt", "a", encoding="utf-8") as f:
    f.write("这是追加的内容\n")

# 文件模式
# 'r'  - 只读(默认)
# 'w'  - 写入(覆盖)
# 'a'  - 追加
# 'x'  - 创建(文件已存在则报错)
# 'r+' - 读写
# 'w+' - 读写(覆盖)
# 'a+' - 读写(追加)
# 'rb' - 二进制读
# 'wb' - 二进制写

# 文件位置操作
with open("example.txt", "r", encoding="utf-8") as f:
    print(f.tell())     # 0(当前位置)
    f.read(5)
    print(f.tell())     # 5
    f.seek(0)           # 回到开头
    print(f.tell())     # 0

8.2 二进制文件操作

# 写入二进制文件
data = b'\x00\x01\x02\x03\x04'
with open("binary.dat", "wb") as f:
    f.write(data)

# 读取二进制文件
with open("binary.dat", "rb") as f:
    data = f.read()
    print(data)  # b'\x00\x01\x02\x03\x04'
    print(list(data))  # [0, 1, 2, 3, 4]

# 使用 struct 处理二进制数据
import struct

# 打包数据
data = struct.pack('if', 42, 3.14)  # int 和 float
print(data)

# 解包数据
num, value = struct.unpack('if', data)
print(num, value)  # 42 3.140000104904175

8.3 CSV 文件操作

import csv

# 写入 CSV
data = [
    ["Name", "Age", "City"],
    ["Alice", 25, "Beijing"],
    ["Bob", 30, "Shanghai"],
    ["Charlie", 35, "Guangzhou"]
]

with open("people.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# 读取 CSV
with open("people.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# 使用字典方式读写
with open("people_dict.csv", "w", newline="", encoding="utf-8") as f:
    fieldnames = ["Name", "Age", "City"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({"Name": "Alice", "Age": 25, "City": "Beijing"})
    writer.writerow({"Name": "Bob", "Age": 30, "City": "Shanghai"})

with open("people_dict.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(dict(row))

8.4 JSON 文件操作

import json

# Python 对象转 JSON
data = {
    "name": "Alice",
    "age": 25,
    "hobbies": ["reading", "coding", "hiking"],
    "address": {
        "city": "Beijing",
        "zip": "100000"
    }
}

# 写入 JSON 文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 读取 JSON 文件
with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)
    print(loaded)

# 字符串转换
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)

parsed = json.loads(json_str)
print(parsed)

8.5 pathlib 模块

from pathlib import Path

# 创建路径对象
p = Path(".")
home = Path.home()
project = Path("/home/user/project")

# 路径操作
file_path = project / "src" / "main.py"
print(file_path)  # /home/user/project/src/main.py

# 路径属性
p = Path("/home/user/document.txt")
print(p.name)       # document.txt
print(p.stem)       # document
print(p.suffix)     # .txt
print(p.parent)     # /home/user
print(p.parts)      # ('/', 'home', 'user', 'document.txt')

# 文件操作
p = Path("test.txt")
p.write_text("Hello, World!", encoding="utf-8")
content = p.read_text(encoding="utf-8")
print(content)

# 目录操作
new_dir = Path("new_directory")
new_dir.mkdir(exist_ok=True)

# 遍历目录
for item in Path(".").iterdir():
    print(item)

# 查找文件
for py_file in Path(".").glob("*.py"):
    print(py_file)

# 递归查找
for py_file in Path(".").rglob("*.py"):
    print(py_file)

# 文件信息
p = Path("test.txt")
print(p.exists())    # True
print(p.is_file())   # True
print(p.is_dir())    # False
print(p.stat().st_size)  # 文件大小

8.6 os 模块

import os

# 当前工作目录
print(os.getcwd())

# 改变目录
# os.chdir("/tmp")

# 列出目录内容
print(os.listdir("."))

# 创建目录
os.makedirs("new/path", exist_ok=True)

# 删除文件
# os.remove("file.txt")

# 删除目录
# os.rmdir("empty_dir")

# 重命名
# os.rename("old.txt", "new.txt")

# 环境变量
print(os.environ.get("HOME"))
print(os.getenv("PATH"))

# 路径操作
print(os.path.join("home", "user", "file.txt"))
print(os.path.exists("file.txt"))
print(os.path.isfile("file.txt"))
print(os.path.isdir("directory"))
print(os.path.basename("/home/user/file.txt"))  # file.txt
print(os.path.dirname("/home/user/file.txt"))   # /home/user
print(os.path.splitext("file.txt"))  # ('file', '.txt')

第九章:异常处理

9.1 基本异常处理

# try-except
try:
    result = 10 / 0
except ZeroDivisionError:
    print("不能除以零!")

# 捕获多个异常
try:
    num = int(input("请输入数字:"))
    result = 10 / num
except ValueError:
    print("输入的不是有效数字!")
except ZeroDivisionError:
    print("不能除以零!")

# 捕获多个异常(合并)
try:
    num = int(input("请输入数字:"))
    result = 10 / num
except (ValueError, ZeroDivisionError) as e:
    print(f"发生错误:{e}")

# 获取异常信息
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"错误类型:{type(e).__name__}")
    print(f"错误信息:{e}")

# try-except-else
try:
    num = int(input("请输入数字:"))
    result = 10 / num
except ValueError:
    print("输入无效")
except ZeroDivisionError:
    print("不能除以零")
else:
    print(f"结果是:{result}")  # 没有异常时执行

# try-except-finally
try:
    f = open("file.txt", "r")
    content = f.read()
except FileNotFoundError:
    print("文件不存在")
finally:
    print("清理工作...")  # 无论如何都会执行

9.2 常见内置异常

# TypeError - 类型错误
# try:
#     result = "hello" + 42
# except TypeError as e:
#     print(e)

# ValueError - 值错误
# try:
#     num = int("abc")
# except ValueError as e:
#     print(e)

# IndexError - 索引错误
# try:
#     lst = [1, 2, 3]
#     print(lst[10])
# except IndexError as e:
#     print(e)

# KeyError - 键错误
# try:
#     d = {"a": 1}
#     print(d["b"])
# except KeyError as e:
#     print(e)

# AttributeError - 属性错误
# try:
#     s = "hello"
#     s.append("x")
# except AttributeError as e:
#     print(e)

# FileNotFoundError - 文件未找到
# try:
#     f = open("nonexistent.txt")
# except FileNotFoundError as e:
#     print(e)

# ImportError - 导入错误
# try:
#     import nonexistent_module
# except ImportError as e:
#     print(e)

# Exception 是所有内置异常的基类
try:
    # 某些可能出错的代码
    pass
except Exception as e:
    print(f"捕获到异常:{e}")

9.3 自定义异常

# 自定义异常类
class CustomError(Exception):
    """自定义异常基类"""
    pass

class ValidationError(CustomError):
    """验证错误"""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"验证失败 - {field}: {message}")

class NotFoundError(CustomError):
    """资源未找到"""
    def __init__(self, resource, identifier):
        self.resource = resource
        self.identifier = identifier
        super().__init__(f"{resource} 未找到:{identifier}")

# 使用自定义异常
def validate_age(age):
    if not isinstance(age, int):
        raise ValidationError("age", "必须是整数")
    if age < 0 or age > 150:
        raise ValidationError("age", "必须在 0-150 之间")
    return True

def find_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    if user_id not in users:
        raise NotFoundError("用户", user_id)
    return users[user_id]

# 测试
try:
    validate_age(-5)
except ValidationError as e:
    print(e)  # 验证失败 - age: 必须在 0-150 之间
    print(f"字段:{e.field}")
    print(f"消息:{e.message}")

try:
    user = find_user(99)
except NotFoundError as e:
    print(e)  # 用户 未找到:99

# 异常链
try:
    try:
        result = 10 / 0
    except ZeroDivisionError as e:
        raise ValueError("计算错误") from e
except ValueError as e:
    print(f"错误:{e}")
    print(f"原因:{e.__cause__}")

9.4 异常最佳实践

# 1. 只捕获你能处理的异常
# 不好
try:
    result = some_operation()
except Exception:
    pass  # 吞掉所有异常

# 好
try:
    result = some_operation()
except SpecificError as e:
    log_error(e)
    result = default_value

# 2. 使用 finally 清理资源
# 不好
f = open("file.txt")
content = f.read()
f.close()  # 如果上面出错,这行不会执行

# 好
f = None
try:
    f = open("file.txt")
    content = f.read()
finally:
    if f:
        f.close()

# 最好(使用 with)
with open("file.txt") as f:
    content = f.read()

# 3. EAFP 原则(Easier to Ask Forgiveness than Permission)
# Python 风格:先做,出错再处理
# 不好(LBYL - Look Before You Leap)
if key in dictionary:
    value = dictionary[key]
else:
    value = default

# 好(EAFP)
try:
    value = dictionary[key]
except KeyError:
    value = default

# 4. 提供有用的错误信息
class ConfigError(Exception):
    def __init__(self, key, expected_type, actual_value):
        self.key = key
        self.expected_type = expected_type
        self.actual_value = actual_value
        super().__init__(
            f"配置错误:'{key}' 应为 {expected_type.__name__} 类型,"
            f"实际值为 {actual_value!r}({type(actual_value).__name__})"
        )

9.5 断言

# assert 语句
def calculate_average(numbers):
    assert len(numbers) > 0, "列表不能为空"
    return sum(numbers) / len(numbers)

# 正常使用
print(calculate_average([1, 2, 3, 4, 5]))  # 3.0

# 触发断言
try:
    calculate_average([])
except AssertionError as e:
    print(f"断言失败:{e}")

# 断言用于调试,不应替代异常处理
# 在生产环境中可以用 -O 标志禁用断言
# python -O script.py

# 自定义断言消息
def set_age(age):
    assert isinstance(age, int), f"age 必须是整数,收到 {type(age).__name__}"
    assert 0 <= age <= 150, f"age 必须在 0-150 之间,收到 {age}"
    return age

# 使用 unittest 进行测试
import unittest

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(1 + 1, 2)
    
    def test_divide_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            1 / 0

if __name__ == "__main__":
    unittest.main()

第十章:常用标准库

10.1 datetime 模块

from datetime import datetime, date, time, timedelta

# 获取当前时间
now = datetime.now()
print(now)  # 2024-01-15 14:30:45.123456

# 创建特定日期时间
dt = datetime(2024, 1, 15, 14, 30, 0)
print(dt)

# 日期时间属性
print(now.year)      # 2024
print(now.month)     # 1
print(now.day)       # 15
print(now.hour)      # 14
print(now.minute)    # 30
print(now.second)    # 45
print(now.weekday()) # 0(周一)

# 格式化
print(now.strftime("%Y-%m-%d %H:%M:%S"))  # 2024-01-15 14:30:45
print(now.strftime("%Y年%m月%d日"))        # 2024年01月15日

# 解析字符串
dt = datetime.strptime("2024-01-15", "%Y-%m-%d")
print(dt)

# 时间运算
tomorrow = now + timedelta(days=1)
last_week = now - timedelta(weeks=1)
print(tomorrow)
print(last_week)

# 两个日期之间的差
d1 = datetime(2024, 1, 1)
d2 = datetime(2024, 12, 31)
diff = d2 - d1
print(f"相差 {diff.days} 天")

# date 和 time 对象
today = date.today()
print(today)

current_time = datetime.now().time()
print(current_time)

10.2 re 模块(正则表达式)

import re

# 基本匹配
text = "我的电话是 13812345678,邮箱是 test@example.com"

# 查找手机号
phone_pattern = r"1[3-9]\d{9}"
phone = re.search(phone_pattern, text)
if phone:
    print(f"手机号:{phone.group()}")

# 查找邮箱
email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
email = re.search(email_pattern, text)
if email:
    print(f"邮箱:{email.group()}")

# 常用函数
text = "Hello 123 World 456 Python 789"

# findall - 查找所有匹配
numbers = re.findall(r"\d+", text)
print(numbers)  # ['123', '456', '789']

# sub - 替换
result = re.sub(r"\d+", "NUM", text)
print(result)  # Hello NUM World NUM Python NUM

# split - 分割
parts = re.split(r"\s+", text)
print(parts)  # ['Hello', '123', 'World', '456', 'Python', '789']

# 分组
text = "2024-01-15"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.match(pattern, text)
if match:
    year, month, day = match.groups()
    print(f"年:{year},月:{month},日:{day}")

# 命名分组
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.match(pattern, text)
if match:
    print(match.group("year"))  # 2024

# 编译正则(提高性能)
email_re = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
emails = email_re.findall("test@example.com and admin@site.org")
print(emails)

# 常用正则模式
# \d  - 数字 [0-9]
# \D  - 非数字
# \w  - 字母数字下划线 [a-zA-Z0-9_]
# \W  - 非字母数字下划线
# \s  - 空白字符
# \S  - 非空白字符
# .   - 任意字符(除换行)
# *   - 0 次或多次
# +   - 1 次或多次
# ?   - 0 次或 1 次
# {n} - 恰好 n 次
# {n,m} - n 到 m 次
# ^   - 开头
# $   - 结尾
# []  - 字符类
# |   - 或
# ()  - 分组

10.3 collections 模块

from collections import Counter, defaultdict, OrderedDict, deque, namedtuple

# Counter - 计数器
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counter = Counter(words)
print(counter)  # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(counter.most_common(2))  # [('apple', 3), ('banana', 2)]

# defaultdict - 默认字典
dd = defaultdict(list)
dd["fruits"].append("apple")
dd["fruits"].append("banana")
dd["vegetables"].append("carrot")
print(dict(dd))

# OrderedDict - 有序字典(Python 3.7+ 普通字典也有序)
od = OrderedDict()
od["b"] = 2
od["a"] = 1
od["c"] = 3

# deque - 双端队列
dq = deque([1, 2, 3])
dq.append(4)        # 右端添加
dq.appendleft(0)    # 左端添加
dq.pop()            # 右端弹出
dq.popleft()        # 左端弹出
dq.rotate(1)        # 旋转

# 固定长度 deque
fixed_dq = deque(maxlen=3)
fixed_dq.append(1)
fixed_dq.append(2)
fixed_dq.append(3)
fixed_dq.append(4)  # 自动丢弃最左边的元素
print(fixed_dq)  # deque([2, 3, 4], maxlen=3)

# namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y)  # 10 20

10.4 itertools 模块

import itertools

# count - 无限计数器
counter = itertools.count(start=1, step=2)
print(next(counter))  # 1
print(next(counter))  # 3
print(next(counter))  # 5

# cycle - 无限循环
colors = itertools.cycle(["red", "green", "blue"])
print(next(colors))  # red
print(next(colors))  # green
print(next(colors))  # blue
print(next(colors))  # red

# chain - 连接迭代器
result = list(itertools.chain([1, 2, 3], [4, 5, 6]))
print(result)  # [1, 2, 3, 4, 5, 6]

# product - 笛卡尔积
result = list(itertools.product("AB", "12"))
print(result)  # [('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')]

# permutations - 排列
result = list(itertools.permutations("ABC", 2))
print(result)  # [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]

# combinations - 组合
result = list(itertools.combinations("ABC", 2))
print(result)  # [('A', 'B'), ('A', 'C'), ('B', 'C')]

# groupby - 分组
data = [("A", 1), ("A", 2), ("B", 3), ("B", 4), ("A", 5)]
data.sort(key=lambda x: x[0])  # 必须先排序
for key, group in itertools.groupby(data, key=lambda x: x[0]):
    print(key, list(group))

# islice - 切片迭代器
result = list(itertools.islice(range(100), 5, 15, 2))
print(result)  # [5, 7, 9, 11, 13]

10.5 functools 模块

import functools

# lru_cache - 缓存装饰器
@functools.lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # 12586269025
print(fibonacci.cache_info())  # 缓存信息

# partial - 偏函数
def power(base, exponent):
    return base ** exponent

square = functools.partial(power, exponent=2)
cube = functools.partial(power, exponent=3)

print(square(5))  # 25
print(cube(3))    # 27

# reduce - 累积计算
numbers = [1, 2, 3, 4, 5]
total = functools.reduce(lambda x, y: x + y, numbers)
print(total)  # 15

# total_ordering - 自动生成比较方法
@functools.total_ordering
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade
    
    def __eq__(self, other):
        return self.grade == other.grade
    
    def __lt__(self, other):
        return self.grade < other.grade

s1 = Student("Alice", 88)
s2 = Student("Bob", 95)
print(s1 < s2)   # True
print(s1 >= s2)  # False(自动生成)

# wraps - 保留被装饰函数的信息
def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """示例函数"""
    pass

print(example.__name__)  # example
print(example.__doc__)   # 示例函数

10.6 hashlib 模块

import hashlib

# MD5
data = "Hello, World!".encode("utf-8")
md5 = hashlib.md5(data).hexdigest()
print(f"MD5: {md5}")

# SHA-1
sha1 = hashlib.sha1(data).hexdigest()
print(f"SHA-1: {sha1}")

# SHA-256
sha256 = hashlib.sha256(data).hexdigest()
print(f"SHA-256: {sha256}")

# 分块计算(大文件)
def file_hash(filename, algorithm="sha256"):
    h = hashlib.new(algorithm)
    with open(filename, "rb") as f:
        while chunk := f.read(8192):
            h.update(chunk)
    return h.hexdigest()

# 密码加盐哈希
import os
import hashlib

def hash_password(password):
    salt = os.urandom(32)
    key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
    return salt + key

def verify_password(stored, password):
    salt = stored[:32]
    stored_key = stored[32:]
    new_key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
    return new_key == stored_key

10.7 random 模块

import random

# 随机整数
print(random.randint(1, 100))     # 1-100 之间的随机整数
print(random.randrange(0, 10, 2)) # 0-9 之间的随机偶数

# 随机浮点数
print(random.random())            # 0-1 之间的随机浮点数
print(random.uniform(1.0, 10.0))  # 1-10 之间的随机浮点数

# 随机选择
colors = ["red", "green", "blue", "yellow"]
print(random.choice(colors))       # 随机选择一个
print(random.choices(colors, k=3)) # 随机选择 3 个(可重复)
print(random.sample(colors, 2))    # 随机选择 2 个(不重复)

# 打乱顺序
deck = list(range(1, 53))
random.shuffle(deck)
print(deck[:5])  # 前 5 张牌

# 设置种子(可复现)
random.seed(42)
print(random.randint(1, 100))  # 每次运行都一样

# 加密安全的随机数
import secrets
token = secrets.token_hex(16)
print(token)

secure_number = secrets.randbelow(100)
print(secure_number)

第十一章:实战项目

11.1 项目一:通讯录管理系统

"""
通讯录管理系统
功能:添加、删除、查找、显示、保存到文件
"""

import json
from pathlib import Path

class ContactManager:
    def __init__(self, filename="contacts.json"):
        self.filename = Path(filename)
        self.contacts = self.load_contacts()
    
    def load_contacts(self):
        """从文件加载联系人"""
        if self.filename.exists():
            with open(self.filename, "r", encoding="utf-8") as f:
                return json.load(f)
        return {}
    
    def save_contacts(self):
        """保存联系人到文件"""
        with open(self.filename, "w", encoding="utf-8") as f:
            json.dump(self.contacts, f, ensure_ascii=False, indent=2)
    
    def add_contact(self, name, phone, email=""):
        """添加联系人"""
        if name in self.contacts:
            print(f"联系人 {name} 已存在")
            return False
        
        self.contacts[name] = {
            "phone": phone,
            "email": email
        }
        self.save_contacts()
        print(f"成功添加联系人:{name}")
        return True
    
    def delete_contact(self, name):
        """删除联系人"""
        if name not in self.contacts:
            print(f"联系人 {name} 不存在")
            return False
        
        del self.contacts[name]
        self.save_contacts()
        print(f"成功删除联系人:{name}")
        return True
    
    def search_contact(self, keyword):
        """查找联系人"""
        results = {}
        for name, info in self.contacts.items():
            if keyword.lower() in name.lower() or keyword in info.get("phone", ""):
                results[name] = info
        
        if results:
            print(f"找到 {len(results)} 个匹配的联系人:")
            for name, info in results.items():
                print(f"  姓名:{name}")
                print(f"  电话:{info['phone']}")
                print(f"  邮箱:{info.get('email', '未设置')}")
                print("-" * 30)
        else:
            print("未找到匹配的联系人")
        
        return results
    
    def list_contacts(self):
        """显示所有联系人"""
        if not self.contacts:
            print("通讯录为空")
            return
        
        print(f"\n共有 {len(self.contacts)} 个联系人:")
        print("=" * 40)
        for name, info in sorted(self.contacts.items()):
            print(f"姓名:{name}")
            print(f"电话:{info['phone']}")
            print(f"邮箱:{info.get('email', '未设置')}")
            print("-" * 40)
    
    def update_contact(self, name, phone=None, email=None):
        """更新联系人信息"""
        if name not in self.contacts:
            print(f"联系人 {name} 不存在")
            return False
        
        if phone:
            self.contacts[name]["phone"] = phone
        if email:
            self.contacts[name]["email"] = email
        
        self.save_contacts()
        print(f"成功更新联系人:{name}")
        return True

def main():
    manager = ContactManager()
    
    while True:
        print("\n===== 通讯录管理系统 =====")
        print("1. 添加联系人")
        print("2. 删除联系人")
        print("3. 查找联系人")
        print("4. 显示所有联系人")
        print("5. 更新联系人")
        print("0. 退出系统")
        print("=" * 25)
        
        choice = input("\n请选择操作:").strip()
        
        if choice == "1":
            name = input("请输入姓名:").strip()
            phone = input("请输入电话:").strip()
            email = input("请输入邮箱(可选):").strip()
            manager.add_contact(name, phone, email)
        
        elif choice == "2":
            name = input("请输入要删除的联系人姓名:").strip()
            confirm = input(f"确认删除 {name}?(y/n):").strip()
            if confirm.lower() == "y":
                manager.delete_contact(name)
        
        elif choice == "3":
            keyword = input("请输入查找关键词:").strip()
            manager.search_contact(keyword)
        
        elif choice == "4":
            manager.list_contacts()
        
        elif choice == "5":
            name = input("请输入要更新的联系人姓名:").strip()
            phone = input("请输入新电话(留空不修改):").strip()
            email = input("请输入新邮箱(留空不修改):").strip()
            manager.update_contact(name, phone or None, email or None)
        
        elif choice == "0":
            print("感谢使用,再见!")
            break
        
        else:
            print("无效选择,请重试")

if __name__ == "__main__":
    main()

11.2 项目二:学生成绩管理系统

"""
学生成绩管理系统
功能:录入成绩、计算统计、排名、导出报表
"""

import csv
from dataclasses import dataclass
from typing import List, Dict
from statistics import mean, median, stdev

@dataclass
class Student:
    student_id: str
    name: str
    scores: Dict[str, float]
    
    @property
    def average(self):
        if not self.scores:
            return 0
        return mean(self.scores.values())
    
    @property
    def total(self):
        return sum(self.scores.values())

class GradeManager:
    def __init__(self):
        self.students: List[Student] = []
        self.subjects: List[str] = []
    
    def set_subjects(self, subjects: List[str]):
        """设置科目"""
        self.subjects = subjects
        print(f"已设置科目:{', '.join(subjects)}")
    
    def add_student(self, student_id: str, name: str):
        """添加学生"""
        # 检查学号是否重复
        if any(s.student_id == student_id for s in self.students):
            print(f"学号 {student_id} 已存在")
            return False
        
        student = Student(student_id=student_id, name=name, scores={})
        self.students.append(student)
        print(f"成功添加学生:{name}")
        return student
    
    def input_scores(self, student_id: str, scores: Dict[str, float]):
        """录入成绩"""
        student = self.find_student(student_id)
        if not student:
            print(f"未找到学号为 {student_id} 的学生")
            return False
        
        for subject, score in scores.items():
            if subject not in self.subjects:
                print(f"未知科目:{subject}")
                continue
            if not 0 <= score <= 100:
                print(f"成绩必须在 0-100 之间:{subject}={score}")
                continue
            student.scores[subject] = score
        
        print(f"已为 {student.name} 录入成绩")
        return True
    
    def find_student(self, student_id: str):
        """查找学生"""
        for student in self.students:
            if student.student_id == student_id:
                return student
        return None
    
    def get_statistics(self, subject: str = None):
        """获取统计数据"""
        if subject:
            scores = [s.scores.get(subject, 0) for s in self.students if subject in s.scores]
            if not scores:
                return None
            return {
                "科目": subject,
                "人数": len(scores),
                "平均分": round(mean(scores), 2),
                "最高分": max(scores),
                "最低分": min(scores),
                "中位数": round(median(scores), 2),
                "标准差": round(stdev(scores), 2) if len(scores) > 1 else 0
            }
        else:
            stats = {}
            for sub in self.subjects:
                sub_stats = self.get_statistics(sub)
                if sub_stats:
                    stats[sub] = sub_stats
            return stats
    
    def get_ranking(self):
        """获取排名"""
        ranked = sorted(self.students, key=lambda s: s.total, reverse=True)
        ranking = []
        for i, student in enumerate(ranked, 1):
            ranking.append({
                "排名": i,
                "学号": student.student_id,
                "姓名": student.name,
                "总分": student.total,
                "平均分": round(student.average, 2)
            })
        return ranking
    
    def export_csv(self, filename: str):
        """导出成绩到 CSV"""
        with open(filename, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.writer(f)
            # 写入表头
            header = ["学号", "姓名"] + self.subjects + ["总分", "平均分"]
            writer.writerow(header)
            # 写入数据
            for student in self.students:
                row = [student.student_id, student.name]
                for subject in self.subjects:
                    row.append(student.scores.get(subject, ""))
                row.extend([student.total, round(student.average, 2)])
                writer.writerow(row)
        print(f"成绩已导出到 {filename}")

def main():
    manager = GradeManager()
    
    # 设置科目
    manager.set_subjects(["语文", "数学", "英语"])
    
    # 添加学生并录入成绩
    students_data = [
        ("001", "张三", {"语文": 85, "数学": 92, "英语": 78}),
        ("002", "李四", {"语文": 90, "数学": 88, "英语": 95}),
        ("003", "王五", {"语文": 76, "数学": 95, "英语": 82}),
        ("004", "赵六", {"语文": 88, "数学": 76, "英语": 90}),
        ("005", "钱七", {"语文": 92, "数学": 85, "英语": 88}),
    ]
    
    for sid, name, scores in students_data:
        student = manager.add_student(sid, name)
        if student:
            manager.input_scores(sid, scores)
    
    # 显示统计信息
    print("\n===== 成绩统计 =====")
    stats = manager.get_statistics()
    for subject, stat in stats.items():
        print(f"\n{subject}:")
        for key, value in stat.items():
            print(f"  {key}: {value}")
    
    # 显示排名
    print("\n===== 成绩排名 =====")
    ranking = manager.get_ranking()
    for r in ranking:
        print(f"第{r['排名']}名:{r['姓名']} "
              f"(总分:{r['总分']},平均分:{r['平均分']})")
    
    # 导出报表
    manager.export_csv("grades.csv")

if __name__ == "__main__":
    main()

11.3 项目三:Web 爬虫基础

"""
简单的网页爬虫
使用 requests 和 BeautifulSoup
注意:需要安装第三方库
pip install requests beautifulsoup4
"""

import requests
from bs4 import BeautifulSoup
import json
import time
from pathlib import Path

class SimpleCrawler:
    def __init__(self, delay=1):
        self.delay = delay
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                         "AppleWebKit/537.36 (KHTML, like Gecko) "
                         "Chrome/91.0.4472.124 Safari/537.36"
        })
    
    def fetch(self, url: str) -> str:
        """获取网页内容"""
        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            response.encoding = response.apparent_encoding
            return response.text
        except requests.RequestException as e:
            print(f"请求失败:{e}")
            return ""
    
    def parse_html(self, html: str) -> BeautifulSoup:
        """解析 HTML"""
        return BeautifulSoup(html, "html.parser")
    
    def extract_links(self, soup: BeautifulSoup, base_url: str = ""):
        """提取所有链接"""
        links = []
        for a_tag in soup.find_all("a", href=True):
            href = a_tag["href"]
            if href.startswith("http"):
                links.append(href)
            elif href.startswith("/") and base_url:
                links.append(base_url + href)
        return links
    
    def extract_text(self, soup: BeautifulSoup):
        """提取正文文本"""
        # 移除 script 和 style 标签
        for tag in soup(["script", "style"]):
            tag.decompose()
        
        text = soup.get_text(separator="\n", strip=True)
        return text
    
    def extract_images(self, soup: BeautifulSoup, base_url: str = ""):
        """提取所有图片"""
        images = []
        for img in soup.find_all("img", src=True):
            src = img["src"]
            alt = img.get("alt", "")
            if src.startswith("http"):
                images.append({"src": src, "alt": alt})
            elif src.startswith("/") and base_url:
                images.append({"src": base_url + src, "alt": alt})
        return images
    
    def save_to_file(self, content: str, filename: str):
        """保存内容到文件"""
        path = Path(filename)
        path.parent.mkdir(parents=True, exist_ok=True)
        with open(path, "w", encoding="utf-8") as f:
            f.write(content)
        print(f"已保存到 {filename}")
    
    def crawl(self, url: str):
        """爬取单个页面"""
        print(f"正在爬取:{url}")
        html = self.fetch(url)
        if not html:
            return None
        
        soup = self.parse_html(html)
        
        result = {
            "url": url,
            "title": soup.title.string if soup.title else "",
            "text": self.extract_text(soup)[:1000],  # 只取前1000字
            "links": self.extract_links(soup),
            "images": self.extract_images(soup)
        }
        
        time.sleep(self.delay)
        return result

# 使用示例
def demo():
    crawler = SimpleCrawler(delay=1)
    
    # 爬取单个页面
    url = "https://example.com"
    result = crawler.crawl(url)
    
    if result:
        print(f"标题:{result['title']}")
        print(f"链接数:{len(result['links'])}")
        print(f"图片数:{len(result['images'])}")
        
        # 保存结果
        crawler.save_to_file(
            json.dumps(result, ensure_ascii=False, indent=2),
            "crawl_result.json"
        )

if __name__ == "__main__":
    demo()

11.4 项目四:文件批量重命名工具

"""
文件批量重命名工具
功能:按规则批量重命名文件
"""

import os
from pathlib import Path
from datetime import datetime
import re

class FileRenamer:
    def __init__(self, directory: str):
        self.directory = Path(directory)
        if not self.directory.is_dir():
            raise ValueError(f"目录不存在:{directory}")
    
    def list_files(self, pattern: str = "*"):
        """列出匹配的文件"""
        files = list(self.directory.glob(pattern))
        files = [f for f in files if f.is_file()]
        return sorted(files)
    
    def preview_rename(self, files, rename_func):
        """预览重命名结果"""
        print("\n预览重命名:")
        print("-" * 60)
        for file in files:
            new_name = rename_func(file)
            if new_name != file.name:
                print(f"  {file.name}  →  {new_name}")
            else:
                print(f"  {file.name}  (不修改)")
        print("-" * 60)
    
    def execute_rename(self, files, rename_func, dry_run=True):
        """执行重命名"""
        renamed = 0
        for file in files:
            new_name = rename_func(file)
            new_path = file.parent / new_name
            
            if new_name == file.name:
                continue
            
            if dry_run:
                print(f"[模拟] {file.name} → {new_name}")
            else:
                if new_path.exists():
                    print(f"[跳过] {new_name} 已存在")
                    continue
                file.rename(new_path)
                print(f"[完成] {file.name} → {new_name}")
            
            renamed += 1
        
        print(f"\n共{'模拟' if dry_run else ''}重命名 {renamed} 个文件")
    
    # 重命名策略
    @staticmethod
    def add_prefix(prefix):
        """添加前缀"""
        return lambda file: f"{prefix}{file.name}"
    
    @staticmethod
    def add_suffix(suffix):
        """添加后缀"""
        return lambda file: f"{file.stem}{suffix}{file.suffix}"
    
    @staticmethod
    def add_number(start=1, step=1, digits=3):
        """添加序号"""
        counter = [start]
        def rename(file):
            name = f"{counter[0]:0{digits}d}_{file.name}"
            counter[0] += step
            return name
        return rename
    
    @staticmethod
    def change_extension(new_ext):
        """修改扩展名"""
        if not new_ext.startswith("."):
            new_ext = "." + new_ext
        return lambda file: file.stem + new_ext
    
    @staticmethod
    def replace_text(old_text, new_text):
        """替换文本"""
        return lambda file: file.name.replace(old_text, new_text)
    
    @staticmethod
    def regex_replace(pattern, replacement):
        """正则替换"""
        compiled = re.compile(pattern)
        return lambda file: compiled.sub(replacement, file.name)
    
    @staticmethod
    def to_lower():
        """转换为小写"""
        return lambda file: file.name.lower()
    
    @staticmethod
    def to_upper():
        """转换为大写"""
        return lambda file: file.name.upper()
    
    @staticmethod
    def add_date(fmt="%Y%m%d"):
        """添加日期"""
        date_str = datetime.now().strftime(fmt)
        return lambda file: f"{date_str}_{file.name}"

def main():
    # 示例:重命名当前目录下的文件
    directory = input("请输入目录路径(默认当前目录):").strip() or "."
    
    try:
        renamer = FileRenamer(directory)
    except ValueError as e:
        print(e)
        return
    
    files = renamer.list_files()
    if not files:
        print("目录中没有文件")
        return
    
    print(f"\n找到 {len(files)} 个文件:")
    for f in files[:10]:
        print(f"  {f.name}")
    if len(files) > 10:
        print(f"  ... 还有 {len(files) - 10} 个文件")
    
    print("\n请选择重命名策略:")
    print("1. 添加前缀")
    print("2. 添加后缀")
    print("3. 添加序号")
    print("4. 替换文本")
    print("5. 转换为小写")
    print("6. 转换为大写")
    print("7. 添加日期前缀")
    
    choice = input("\n请选择:").strip()
    
    rename_func = None
    
    if choice == "1":
        prefix = input("请输入前缀:")
        rename_func = FileRenamer.add_prefix(prefix)
    elif choice == "2":
        suffix = input("请输入后缀:")
        rename_func = FileRenamer.add_suffix(suffix)
    elif choice == "3":
        start = int(input("起始序号(默认1):") or "1")
        digits = int(input("序号位数(默认3):") or "3")
        rename_func = FileRenamer.add_number(start=start, digits=digits)
    elif choice == "4":
        old = input("要替换的文本:")
        new = input("替换为:")
        rename_func = FileRenamer.replace_text(old, new)
    elif choice == "5":
        rename_func = FileRenamer.to_lower()
    elif choice == "6":
        rename_func = FileRenamer.to_upper()
    elif choice == "7":
        rename_func = FileRenamer.add_date()
    else:
        print("无效选择")
        return
    
    # 预览
    renamer.preview_rename(files, rename_func)
    
    # 确认执行
    confirm = input("\n确认执行?(y/n/模拟):").strip().lower()
    if confirm == "y":
        renamer.execute_rename(files, rename_func, dry_run=False)
    elif confirm == "模拟":
        renamer.execute_rename(files, rename_func, dry_run=True)
    else:
        print("已取消")

if __name__ == "__main__":
    main()

11.5 项目五:简单的 REST API

"""
使用 Flask 创建简单的 REST API
注意:需要安装 Flask
pip install flask
"""

from flask import Flask, jsonify, request
from datetime import datetime

app = Flask(__name__)

# 模拟数据库
todos = [
    {"id": 1, "title": "学习 Python", "completed": True, "created_at": "2024-01-15"},
    {"id": 2, "title": "完成项目", "completed": False, "created_at": "2024-01-16"},
]

next_id = 3

@app.route("/")
def index():
    return jsonify({
        "message": "Todo API",
        "endpoints": {
            "GET /todos": "获取所有待办事项",
            "GET /todos/<id>": "获取单个待办事项",
            "POST /todos": "创建待办事项",
            "PUT /todos/<id>": "更新待办事项",
            "DELETE /todos/<id>": "删除待办事项"
        }
    })

@app.route("/todos", methods=["GET"])
def get_todos():
    # 查询参数:completed=true/false
    completed = request.args.get("completed")
    if completed is not None:
        completed = completed.lower() == "true"
        filtered = [t for t in todos if t["completed"] == completed]
        return jsonify(filtered)
    return jsonify(todos)

@app.route("/todos/<int:todo_id>", methods=["GET"])
def get_todo(todo_id):
    todo = next((t for t in todos if t["id"] == todo_id), None)
    if todo is None:
        return jsonify({"error": "待办事项不存在"}), 404
    return jsonify(todo)

@app.route("/todos", methods=["POST"])
def create_todo():
    global next_id
    
    if not request.json or "title" not in request.json:
        return jsonify({"error": "缺少 title 字段"}), 400
    
    todo = {
        "id": next_id,
        "title": request.json["title"],
        "completed": request.json.get("completed", False),
        "created_at": datetime.now().strftime("%Y-%m-%d")
    }
    
    todos.append(todo)
    next_id += 1
    
    return jsonify(todo), 201

@app.route("/todos/<int:todo_id>", methods=["PUT"])
def update_todo(todo_id):
    todo = next((t for t in todos if t["id"] == todo_id), None)
    if todo is None:
        return jsonify({"error": "待办事项不存在"}), 404
    
    if not request.json:
        return jsonify({"error": "请求体不能为空"}), 400
    
    if "title" in request.json:
        todo["title"] = request.json["title"]
    if "completed" in request.json:
        todo["completed"] = request.json["completed"]
    
    return jsonify(todo)

@app.route("/todos/<int:todo_id>", methods=["DELETE"])
def delete_todo(todo_id):
    global todos
    todo = next((t for t in todos if t["id"] == todo_id), None)
    if todo is None:
        return jsonify({"error": "待办事项不存在"}), 404
    
    todos = [t for t in todos if t["id"] != todo_id]
    return jsonify({"message": "删除成功"})

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "资源不存在"}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "服务器内部错误"}), 500

if __name__ == "__main__":
    app.run(debug=True, port=5000)

# 测试命令:
# curl http://localhost:5000/todos
# curl http://localhost:5000/todos/1
# curl -X POST -H "Content-Type: application/json" -d '{"title": "新任务"}' http://localhost:5000/todos
# curl -X PUT -H "Content-Type: application/json" -d '{"completed": true}' http://localhost:5000/todos/1
# curl -X DELETE http://localhost:5000/todos/1

第十二章:常见问题与总结

12.1 常见错误与解决

# 1. IndentationError - 缩进错误
# Python 使用缩进表示代码块,必须保持一致
# 错误示例:
# def func():
#     print("hello")
#      print("world")  # 缩进不一致

# 2. NameError - 名称未定义
# 变量在使用前必须先定义
# 错误示例:
# print(x)  # x 未定义

# 3. TypeError - 类型错误
# 错误示例:
# result = "hello" + 42  # 字符串不能与整数相加
# 正确做法:
# result = "hello" + str(42)

# 4. IndexError - 索引越界
# 错误示例:
# lst = [1, 2, 3]
# print(lst[10])  # 索引超出范围

# 5. KeyError - 字典键不存在
# 错误示例:
# d = {"a": 1}
# print(d["b"])  # 键不存在
# 正确做法:
# print(d.get("b", "default"))

# 6. AttributeError - 属性不存在
# 错误示例:
# s = "hello"
# s.append("x")  # 字符串没有 append 方法

# 7. ValueError - 值错误
# 错误示例:
# num = int("abc")  # 无法转换为整数

# 8. ImportError - 导入错误
# 错误示例:
# import nonexistent_module

# 9. FileNotFoundError - 文件不存在
# 错误示例:
# f = open("nonexistent.txt")

# 10. ZeroDivisionError - 除以零
# 错误示例:
# result = 10 / 0

12.2 Python 编码最佳实践

# 1. 使用有意义的变量名
# 不好
# x = 5
# 好
# max_retries = 5

# 2. 遵循 PEP 8 编码规范
# - 使用 4 个空格缩进
# - 行长度不超过 79 个字符
# - 函数之间空两行
# - 类之间空两行
# - 使用 snake_case 命名变量和函数
# - 使用 CamelCase 命名类
# - 使用 UPPER_CASE 命名常量

# 3. 使用列表推导式(适当情况下)
# 不好
# squares = []
# for x in range(10):
#     squares.append(x ** 2)
# 好
# squares = [x ** 2 for x in range(10)]

# 4. 使用 with 语句管理资源
# 不好
# f = open("file.txt")
# content = f.read()
# f.close()
# 好
# with open("file.txt") as f:
#     content = f.read()

# 5. 使用 f-string 进行字符串格式化
# 不好
# msg = "Hello, %s! You are %d years old." % (name, age)
# 好
# msg = f"Hello, {name}! You are {age} years old."

# 6. 使用 enumerate() 而不是 range(len())
# 不好
# for i in range(len(items)):
#     print(i, items[i])
# 好
# for i, item in enumerate(items):
#     print(i, item)

# 7. 使用 zip() 并行遍历
# 不好
# for i in range(len(names)):
#     print(names[i], ages[i])
# 好
# for name, age in zip(names, ages):
#     print(name, age)

# 8. 使用 get() 访问字典
# 不好
# if key in d:
#     value = d[key]
# 好
# value = d.get(key, default_value)

# 9. 使用异常处理而不是条件检查(EAFP)
# 不好
# if hasattr(obj, "attr"):
#     value = obj.attr
# 好
# try:
#     value = obj.attr
# except AttributeError:
#     value = default

# 10. 编写文档字符串
def calculate_bmi(weight, height):
    """
    计算 BMI(身体质量指数)。
    
    参数:
        weight (float): 体重,单位:千克
        height (float): 身高,单位:米
    
    返回:
        float: BMI 值
    
    示例:
        >>> calculate_bmi(70, 1.75)
        22.86
    """
    return round(weight / (height ** 2), 2)

12.3 学习资源推荐

  1. 官方文档:https://docs.python.org/3/
  2. Python 教程:https://docs.python.org/3/tutorial/
  3. PEP 8 编码规范:https://peps.python.org/pep-0008/
  4. Real Python:https://realpython.com/
  5. 廖雪峰 Python 教程:https://liaoxuefeng.com/
  6. LeetCode:https://leetcode.com/(练习算法)
  7. GitHub:https://github.com/(阅读开源项目)

12.4 下一步学习建议

  1. Web 开发:学习 Flask 或 Django 框架
  2. 数据分析:学习 NumPy、Pandas、Matplotlib
  3. 机器学习:学习 Scikit-learn、TensorFlow 或 PyTorch
  4. 自动化:学习 Selenium、Scrapy
  5. DevOps:学习 Docker、Kubernetes、CI/CD
  6. 游戏开发:学习 Pygame
  7. 桌面应用:学习 Tkinter 或 PyQt

12.5 总结

恭喜你完成了 Python 入门教程的学习!让我们回顾一下本教程涵盖的内容:

  1. 环境搭建:安装 Python、选择 IDE、编写第一个程序
  2. 基础语法:变量、数据类型、运算符、表达式
  3. 控制流:条件语句、循环语句、循环控制
  4. 函数:定义、参数、返回值、作用域、装饰器
  5. 数据结构:列表、元组、字典、集合
  6. 面向对象:类、继承、多态、魔术方法
  7. 文件操作:文本文件、CSV、JSON、pathlib
  8. 异常处理:try-except、自定义异常、最佳实践
  9. 标准库:datetime、re、collections、itertools
  10. 实战项目:通讯录、成绩管理、爬虫、文件重命名、REST API

Python 是一门功能强大且易于学习的编程语言。掌握了这些基础知识后,你已经具备了进一步深入学习的能力。记住,编程是一项实践性很强的技能,多写代码、多做项目是提高编程能力的最佳途径。

祝你在 Python 的世界里编程愉快!🚀


本教程内容仅供参考学习,建议结合实际项目练习加深理解。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录