# 《从 Gopher 到 Pythonista，Python 快速上手指南》- 学习笔记1


## 概述

对的，这是一篇学习笔记，和上一篇没啥关系。

此刻，我写不来 Python，我也记不得多少关于 Python 的知识点。上次写 Python 代码是2017年，上次有学 Python 的想法是 2023 年，不过只学了半天。

今天，准备从0到1，快速入个门。

## 典型项目结构

一个 Python 项目的典型结构入下：

```bash
project_name/                        # 项目根目录
├── project_name/                    # 源码包 (Package) - 核心代码存放地
│   ├── __init__.py                  # 标识此目录为Python包，可包含包初始化代码或定义__all__列表
│   ├── module1.py                   # 模块1：包含相关函数、类等
│   ├── module2.py
│   └── subpackage1/                 # 子包1，用于更深层次的模块化组织
│       ├── __init__.py
│       ├── module3.py
│       └── module4.py
├── tests/                           # 测试目录 - 存放所有单元测试和集成测试
│   ├── __init__.py
│   ├── test_module1.py              # 针对module1的测试
│   ├── test_module2.py
│   └── test_subpackage1/            # 针对子包的测试也可以有相应目录
│       ├── __init__.py
│       └── test_module3.py
├── docs/                            # 项目文档目录 - 使用Sphinx等工具生成详细文档
│   ├── conf.py
│   ├── index.rst
│   └── ...
├── scripts/                         # 脚本目录 - 存放可执行脚本、工具脚本
│   ├── data_cleaning.py
│   ├── train_model.py
│   └── start_server.py
├── data/                           # （可选）数据目录 - 存放项目所需或生成的数据
│   ├── input/
│   └── output/
├── examples/                       # （可选）示例目录 - 存放使用项目的示例代码
├── requirements.txt                # 项目依赖清单 - 列明项目运行所需的所有第三方库及其版本
├── requirements_dev.txt            # （可选）开发环境额外依赖（如测试、构建、文档工具）
├── setup.py                        # 打包和安装脚本 - 用于将项目打包分发给其他人安装使用
├── pyproject.toml                  # 现代项目构建配置文件 (遵循PEP 518)
├── README.md                       # 项目说明文档 - 简介、安装、快速使用等
├── LICENSE                         # 项目许可证文件 - 明确授权条款
└── .gitignore                      # Git忽略规则 - 指定哪些文件/目录不应纳入版本控制
```

## 模块代码结构

一个 py 文件就是一个模块，模块代码的整体布局如下：

```python
"""
This module provides some example classes and functions to demonstrate Python code layout and style.
"""

__version__ = '0.1.0'
__author__ = 'Jane Developer'
__all__ = ['MyClass', 'my_function', 'CONSTANT1']

# Standard Library Imports
import os
import sys

# Third-Party Imports
import requests

# Local Application/Library Specific Imports
from myutils.helpers import format_data

# Global constants
CONSTANT1 = 10
DEFAULT_TIMEOUT = 30

class MyClass:
    """
    A brief description of MyClass.

    More detailed description of the class's purpose and functionality.
    """

    CLASS_CONSTANT = 'some_value'

    def __init__(self, name):
        self.name = name

    def method_one(self):
        """Does a specific thing."""
        pass

    def method_two(self):
        """Does another thing."""
        pass

def my_function(param1, param2=None):
    """
    A brief description of what this function does.

    Args:
        param1 (str): Description of the first parameter.
        param2 (int, optional): Description of the second parameter. Defaults to None.

    Returns:
        bool: Description of the return value.

    Raises:
        ValueError: Under what conditions it's raised.
    """
    if not param1:
        raise ValueError("param1 cannot be empty.")
    
    result = do_something_complex(param1, param2)
    return result

def _private_helper_function():
    """A non-public function (indicated by the leading underscore)."""
    pass

# Main execution block
if __name__ == "__main__":
    # Code here runs only when the module is executed directly,
    # not when it is imported.
    instance = MyClass("test")
    result = my_function("hello")
    print(result)
```

## 包和模块的导入

代码可以通过包和模块组织，那么跨包或者跨模块调用代码是，就需要 import。Python 里的 import 可以导入包、导入模块、可以导入任何定义在模块顶级的名称，包括变量、常量、函数、类等。

假如有这样一个项目：

```python
my_project/
├── main.py           # 主程序，用于演示导入
└── my_package/       # 自定义的包
    ├── __init__.py   # 包的初始化文件
    ├── module_a.py   # 包内的一个模块
    └── module_b.py   # 包内的另一个模块
```

各个文件内容如下：

1. `my_package/__init__.py​`

```python
print("执行 my_package 的 __init__.py 文件")
# 定义一些包级别的变量或执行初始化代码
PACKAGE_NAME = "my_package"
VERSION = "1.0"

# 可以选择将包内模块的特定内容“提升”到包级别，方便用户直接访问
# from .module_a import hello_from_a  # (先注释掉，待会演示)
# from .module_b import hello_from_b  # (先注释掉，待会演示)
```

2. `my_package/module_a.py​`

```python
print("执行 module_a.py 模块")

def hello_from_a():
    return "Hello from Module A!"
```

3. `my_package/module_b.py`

```python
print("执行 module_b.py 模块")

def hello_from_b():
    return "Hello from Module B!"
```

4. `main.py`

```python
print("--- 开始执行 main.py ---")

print("\n1. 导入整个包 my_package")
import my_package  # 这会执行 my_package/__init__.py

print("\n2. 从包中导入特定模块 module_a")
from my_package import module_a  # 由于包已被导入，__init__.py 不会再次执行，但会执行 module_a.py

print("\n3. 从模块中导入特定函数")
from my_package.module_b import hello_from_b  # 导入包内模块的具体函数
print(hello_from_b())  # 调用函数

print("\n--- main.py 执行结束 ---")
```

最后的执行结果是这样的：

```bash
--- 开始执行 main.py ---

1. 导入整个包 my_package
执行 my_package 的 __init__.py 文件

2. 从包中导入特定模块 module_a
执行 module_a.py 模块

3. 从模块中导入特定函数
执行 module_b.py 模块
Hello from Module B!

--- main.py 执行结束 ---
```

## 依赖管理与虚拟环境

当你用 pip install 安装一个 Python 包的时候，这个三方包会安装在同一个目录里（藏着你电脑里的某个 site-packages 目录下），比如 `/opt/homebrew/lib/python3.11/site-packages`，那么当你本地有多个项目依赖同一个包时，这个包的版本就被迫要保持一致了。于是乎，“依赖冲突”问题、“环境隔离与污染”问题接踵而至。甚至你要运行的多个项目依赖的 Python 版本也不尽相同。

所以你需要给每个项目准备单独的 Python 环境。

### 玩法一：pip + venv (Python 内置)

Python 标准库自带的轻量级组合，简单直接。

- ​管理工具: pip(包安装) + venv(环境隔离)
- ​虚拟环境: 需手动创建和管理 (python -m venv .venv)
- ​依赖记录: 使用 requirements.txt文件，通过 pip freeze > requirements.txt生成，通过 pip install -r requirements.txt安装。​它仅记录包列表，无法保证深层依赖的版本绝对一致。
- ​优点: Python 自带，无需额外安装；简单灵活。
- ​缺点: 功能相对基础，依赖解析和冲突解决能力较弱；需要手动管理环境和依赖文件。
- ​适用场景: 快速尝试、运行简单脚本或初学者学习

### 玩法二：Conda (跨语言管理)

强大的开源包管理和环境管理系统，尤其擅长处理数据科学领域的复杂依赖。

- ​管理工具: conda(或更快的 mamba)
- ​虚拟环境: ​内置，自动管理 (conda create -n myenv python=3.10)
- ​依赖记录: 使用 environment.yml文件，通过 conda env export > environment.yml导出环境。
- ​核心优势: 能够管理非Python依赖​（如C/C++库、R语言、CUDA工具链），预构建的二进制包丰富，解决复杂科学计算依赖的能力非常突出。
- ​缺点: 包体积通常较大；生态更侧重于数据科学领域。
- ​适用场景: ​数据科学、机器学习、生物信息学等需要复杂计算栈或跨语言环境的项目

### 玩法三：Poetry (现代一体化)

现代化的全能工具，集依赖管理、虚拟环境、打包发布于一身的项目生命周期管理工具。

- ​管理工具: poetry
- ​虚拟环境: ​内置，自动创建和管理（也可指定现有环境）。
- ​依赖记录: 使用 ​pyproject.toml​ (PEP 621标准) 声明依赖和项目元数据，并生成 ​poetry.lock​ 文件精确锁定所有次级依赖的版本，确保环境绝对可复现。
- ​核心优势: ​依赖解析能力强；统一配置​（一切都在 pyproject.toml中）；原生支持打包和发布到PyPI；严格的项目结构建议。
- ​缺点: 需要单独安装；对某些极特殊的非PyPI依赖处理稍显复杂。
- ​适用场景: ​纯Python的应用程序、库开发，尤其适合团队协作和追求项目规范与可复现性的场景。

## Pip + venv 的用法示例

实例：创建一个简单的 Web 爬虫项目

1. 创建虚拟环境

```bash
mkdir py-projects
cd py-projects
mkdir my-web-crawler
cd my-web-crawler
python -m venv .venv
```

此时你的 my-web-crawler 目录内会包含如下内容：

```bash
.venv
├── bin
├── include
├── lib
└── pyvenv.cf
```

激活虚拟环境后，`python` 和 `pip` 命令就会使用 .venv 里的：

```bash
source .venv/bin/activate
```

然后 `python3` 命令就指向了当前项目的 venv 里的 `python3`

```bash
# ls .venv/bin
Activate.ps1  activate.csh  pip           pip3.11       python3
activate      activate.fish pip3          python        python3.11

# which python3
/Users/danielhu/Work/py-projects/my-web-crawler/.venv/bin/python3
```

2. 安装依赖，编写代码

接着安装 `requests` 包：

```bash
pip install requests
```

此时 `requests` 会被安装到 `.venv/lib/python3.11/site-packages/requests` 路径。

新建一个 `main.py` 文件，写入代码：

```python
import requests
from bs4 import BeautifulSoup

url = 'https://www.danielhu.cn/'

try:
    response = requests.get(url)
    response.raise_for_status() # 检查请求是否成功

    # 使用 BeautifulSoup 解析 HTML 并获取页面标题
    soup = BeautifulSoup(response.text, 'html.parser')
    page_title = soup.title.string if soup.title else 'No title found'
    print(f"The title of '{url}' is: {page_title}")

except requests.RequestException as e:
    print(f"An error occurred while fetching the URL: {e}")
```

安装 `beautifulsoup4`：

```python
pip install beautifulsoup4
```

3. 执行代码

```bash
# python main.py
The title of 'https://www.danielhu.cn/' is: 胡涛的个人网站 | Seven Coffee Cups
```

生成依赖清单：

```bash
pip freeze > requirements.txt
```

此时 requirements.txt 内容如下：

```bash
beautifulsoup4==4.13.5
certifi==2025.8.3
charset-normalizer==3.4.3
idna==3.10
requests==2.32.5
soupsieve==2.8
typing_extensions==4.15.0
urllib3==2.5.0
```

这些包都在 `.venv/lib/python3.11/site-packages/requests` 里面。

4. 退出虚拟环境

需要退出虚拟环境，可以直接执行 `deactivate`

5. 复现环境

现在将依赖已经全部维护到了 requirements.txt 文件里，这个 requirements.txt 会跟着源代码一起上库，但是 .venv 目录显然是不适合丢到代码库的。

当你下载源代码到一个新环境的时候，就可以通过 requirements.txt 里记录依赖清单来安装依赖，将项目跑起来：

```bash
pip install -r requirements.txt
```

## 依赖管理小结

对于一般项目而言，pip + venv 的方式管理依赖和虚拟环境已经够用了。如果是开发机器学习相关的项目，Conda 更合适，因为可以直接管理类似 cudatoolkit 这种非 Python 语言的依赖包。

至于 Poetry，其提供了强大的依赖管理能力（包括次级依赖的版本）和打包发布能力，对于大型项目需要多人协作的场景而言，有其优势。后面有机会再具体研究 Poetry 的用法。

## 通过一个简单的 Flask 应用学习 Python 基础语法

### 创建项目，下载依赖

```bash
mkdir my-first-flask-app
cd my-first-flask-app

python -m venv .venv
source .venv/bin/activate

pip install Flask
```

### 编写 app 模块

创建 `app.py` 文件，注释里面包含了详细的解释。

部分细节（比如关于类和函数更多的讨论）后面单独展开。

```python
# 导入模块：从 flask 包里导入了 Flask 类和 render_template 函数
from flask import Flask, render_template

# 创建一个 Flask 应用实例，这是构建任何 Flask 应用的第一步。
# `__name__` 是一个特殊的 Python 变量，它表示当前模块的名称。
# Python 里的模块名也就是 py 源文件的文件名，比如 app.py 这个文件的模块名就是 app
app = Flask(__name__)

# 定义常量，约定不能修改，但这不是强限制，只是约定俗成。命名风格为大写，下划线连接。
WELCOME_MESSAGE = "Hello, Python and Flask World!"
# 定义变量，命名风格为小写，下划线连接。
website_visits = 0


# 定义一个函数。
def generate_greeting(name):
    """
    这是一个生成问候语的函数。
    它接受一个参数 `name`，并返回一个拼接好的字符串。
    """
    # 字符串拼接：使用加号 (+) 或 f-string（更现代、推荐的方式）将变量嵌入字符串。
    return f"Hello, {name}! Welcome to your first Flask app."


# 使用装饰器定义路由：`@app.route('/')` 告诉 Flask，当用户访问网站根目录时，由下面的函数处理。
@app.route('/')
def hello_world():
    # 使用 `global` 关键字声明我们要修改的是在函数外部定义的全局变量 `website_visits`。
    global website_visits
    website_visits += 1

    # 1. 调用函数，将返回值赋给变量 `greeting`
    greeting = generate_greeting("New Developer")

    # 2. 使用条件语句 (if-elif-else)
    # 根据访问次数显示不同的信息
    if website_visits == 1:
        visit_message = "This is your first visit. Awesome!"
    elif website_visits < 5:
        visit_message = f"You have visited this page {website_visits} times."
    else:
        visit_message = f"Wow! You've been here {website_visits} times! "

    # 3. 使用列表（一种Python数据结构）和循环 (for loop)
    features = [
        "Variables and Constants",
        "Conditional Statements (if/elif/else)",
        "Loops (for)",
        "Functions",
        "Importing Modules"
    ]

    # 创建一个空列表，用于存储处理后的功能项
    numbered_features = []
    # 使用 `enumerate` 在循环中获取每个元素的索引和值
    for index, item in enumerate(features, start=1):
        # 在循环内部进行条件判断和字符串操作
        featured_item = f"Feature {index}: {item}"
        if "Function" in item:  # 检查字符串中是否包含"Function"
            featured_item += " 👩‍💻"  # 如果包含，就添加一个表情符号
        numbered_features.append(featured_item)  # 将处理好的字符串添加到新列表中

    # 使用 `render_template` 函数将数据渲染到HTML模板中。
    return render_template('index.html',
                           welcome_message=WELCOME_MESSAGE,
                           greeting=greeting,
                           visit_message=visit_message,
                           features_list=numbered_features)


# 这是Python中常见的惯用法。
# 它确保只有当这个脚本被直接运行时（而不是被其他模块导入时），Flask开发服务器才会启动。
if __name__ == '__main__':
    # 启动Flask自带的开发服务器，`debug=True` 开启了调试模式，方便开发时看到错误详情。
    app.run(debug=True)
```

### 创建 index.html

Flask 使用 Jinja2 模板引擎来动态生成 HTML。创建一个名为 templates的文件夹，然后在里面创建一个 `index.html` 文件。

（这块内容不重要，只是为了展示完整的 Flask 应用跑起来的效果。实际项目里基本是前后端分离，html 代码轮不到 Python 来渲染。）

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My First Flask App - Learning Python</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }
        h1 { color: #333; }
        h2 { color: #555; }
        ul { list-style-type: none; padding-left: 0; }
        li { margin-bottom: 10px; padding: 8px; background-color: #f4f4f4; border-left: 4px solid #007acc; }
    </style>
</head>
<body>
    <h1>{{ welcome_message }}</h1> <!-- 双花括号用于输出变量的值 -->
    <p><strong>{{ greeting }}</strong></p>
    <p><em>{{ visit_message }}</em></p>

    <h2>Python Basics You're Seeing in Action:</h2>
    <ul>
        <!-- 这是在模板中使用 for 循环，遍历我们传递过来的 features_list 列表 -->
        {% for feature in features_list %}
            <li>{{ feature }}</li> <!-- 循环内的每一次迭代都会生成一个 <li> 标签 -->
        {% endfor %} <!-- 循环结束 -->
    </ul>
</body>
</html>
```

最后执行 `python app.py` 就可以将这个应用启动起来，实际浏览器访问结果就不贴截图了。

### 小结

这里涉及到的循环、函数、类，看起来还有很多知识点有待进一步学习。下面继续挨个看。

## 可迭代对象

Python 中的可迭代对象 (Iterable)​​ 是指那些能够一次返回一个元素，并且可以被 for 循环遍历的对象。在继续详细学习 for 循环之前，我们先来看“可迭代对象”有哪些：

| 类型 | 示例 | `for` 循环遍历结果 | 说明 |
| :--- | :--- | :--- | :--- |
| **列表 (List)** | `[1, 2, 3]` | 逐个返回元素 `1`, `2`, `3` | 有序，可变 |
| **元组 (Tuple)** | `(1, 2, 3)` | 逐个返回元素 `1`, `2`, `3` | 有序，不可变 |
| **字符串 (String)** | `"abc"` | 逐个返回字符 `'a'`, `'b'`, `'c'` | 有序，不可变 |
| **字典 (Dict)** | `{'a': 1, 'b': 2}` | 默认逐个返回**键 (key)** `'a'`, `'b'` | 无序 (Python 3.6+ 后有序)，也可用 `.values()` 迭代值，`.items()` 迭代键值对 |
| **集合 (Set)** | `{1, 2, 3}` | 逐个返回元素 `1`, `2`, `3` (顺序不定) | 无序，元素唯一 |
| **range 对象** | `range(5)` | 逐个返回整数 `0`, `1`, `2`, `3`, `4` | 惰性生成序列，节省内存 |
| **生成器 (Generator)** | `(x for x in range(3))` 或 带 `yield` 的函数 | 逐个返回值 `0`, `1`, `2` | **惰性计算**，**极其节省内存**，适合大量或无限数据 |
| **文件对象 (File Object)** | `open('file.txt')` | 逐行返回文件内容 | 推荐用 `with` 语句打开 |
| **enumerate 对象** | `enumerate(['a', 'b'])` | 返回索引-元素对 `(0, 'a')`, `(1, 'b')` | 常用于获取元素及其索引 |
| **zip 对象** | `zip([1, 2], ['a', 'b'])` | 返回元组 `(1, 'a')`, `(2, 'b')` | 将多个可迭代对象“压缩”并行迭代 |
| **字典视图对象** | `dict.keys()`, `dict.values()`, `dict.items()` | 分别迭代键、值或 (键, 值) 对 | 动态反映字典的变化 |

## 循环(for,while,...)

见下例：

```python
# 综合演示 Python 循环特性

# 1. 基本的 for 循环：遍历序列（如列表）
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:  # 逐个取出列表中的元素
    print(fruit, end=' ')  # 输出: apple banana cherry
print("\n" + "-"*40)

# 2. 使用 range() 控制循环次数
for i in range(3):  # 生成序列 0, 1, 2
    print(f"Index: {i}", end=' ')  # 输出: Index: 0 Index: 1 Index: 2
print("\n" + "-"*40)

# 3. 使用 break 提前终止循环
for num in range(5):
    if num == 3:
        break  # 立即退出整个循环
    print(num, end=' ')  # 输出: 0 1 2
print("\n" + "-"*40)

# 4. 使用 continue 跳过当前迭代
for num in range(5):
    if num == 2:
        continue  # 跳过本次循环剩余代码，进入下一次
    print(num, end=' ')  # 输出: 0 1 3 4 (跳过了2)
print("\n" + "-"*40)

# 5. 循环中的 else 子句
for i in range(3):
    print(i, end=' ')
else:  # 当循环正常结束（未被 break 中断）时执行
    print("-> Loop completed normally")  # 会执行
print("-"*40)

# 搜索示例：break 后 else 不会执行
search_list = [1, 2, 3, 4, 5]
target = 3
for item in search_list:
    if item == target:
        print(f"Found {target}!")
        break
else:  # 由于循环被 break 终止，这里的 else 不会执行
    print(f"{target} not found.")
print("-"*40)

# 6. While 循环：基于条件重复
count = 0
while count < 3:  # 当条件为 True 时执行
    print(f"Count: {count}", end=' ')
    count += 1  # 更新条件变量，避免无限循环
print("\n" + "-"*40)

# 7. 嵌套循环
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:  # 外层循环遍历每一行
    for element in row:  # 内层循环遍历行中的每个元素
        print(element, end=' ')
    print()  # 每行结束后换行
# 输出:
# 1 2 3
# 4 5 6
# 7 8 9
print("-"*40)

# 8. 使用 enumerate() 获取索引和值
fruits = ["apple", "banana", "mango"]
for index, fruit in enumerate(fruits):  # 同时获取索引和值
    print(f"Index {index}: {fruit}")
# 输出:
# Index 0: apple
# Index 1: banana
# Index 2: mango
print("-"*40)

# 9. 遍历字典
person = {"name": "Alice", "age": 30, "city": "New York"}
for key in person:  # 遍历键
    print(key, end=' ')  # 输出: name age city
print()
for key, value in person.items():  # 同时遍历键和值
    print(f"{key}: {value}")
# 输出:
# name: Alice
# age: 30
# city: New York
print("-"*40)

# 10. 使用 zip() 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):  # 将两个列表对应元素组合
    print(f"{name} is {age} years old.")
# 输出:
# Alice is 25 years old.
# Bob is 30 years old.
# Charlie is 35 years old.
```

## 列表推导式

```python
# 基础：快速生成列表
squares = [x**2 for x in range(5)] # 计算 0-4 的平方
print(squares) # 输出: [0, 1, 4, 9, 16]

# 带条件过滤：只处理满足条件的元素
even_squares = [x**2 for x in range(10) if x % 2 == 0] # 只计算偶数的平方
print(even_squares) # 输出: [0, 4, 16, 36, 64]

# 嵌套循环：模拟笛卡尔积
pairs = [(x, y) for x in ['A', 'B'] for y in [1, 2]] # 组合所有可能
print(pairs) # 输出: [('A', 1), ('A', 2), ('B', 1), ('B', 2)]

# 处理字符串：将列表中的单词转为大写
words = ["hello", "world", "python"]
upper_words = [word.upper() for word in words]
print(upper_words) # 输出: ['HELLO', 'WORLD', 'PYTHON']
```

## 字典推导式

```python
# 快速创建字典：数字映射到它的平方
square_dict = {x: x**2 for x in range(5)}
print(square_dict) # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 交换键值对
original_dict = {'a': 1, 'b': 2, 'c': 3}
swapped_dict = {v: k for k, v in original_dict.items()}
print(swapped_dict) # 输出: {1: 'a', 2: 'b', 3: 'c'}

# 基于条件过滤字典项
filtered_dict = {k: v for k, v in original_dict.items() if v > 1}
print(filtered_dict) # 输出: {'b': 2, 'c': 3}
```

## 集合推导式

```python
# 从列表中创建去重后的集合
numbers = [1, 2, 2, 3, 4, 4, 4, 5]
unique_squares = {x**2 for x in numbers} # 计算平方并去重
print(unique_squares) # 输出: {1, 4, 9, 16, 25}

# 从字符串中创建不重复的字符集合
word = "mississippi"
unique_letters = {char for char in word}
print(unique_letters) # 输出: {'i', 'm', 'p', 's'}
```

## 迭代器

### 理解迭代器

Python 中的迭代器 (Iterator) 是用于惰性遍历数据集合的对象，它一次只产生一个元素，以实现内存节省。

```python
# 创建一个自定义迭代器，生成一个有限的数字序列
class MyRangeIterator:
    def __init__(self, start, end):
        self.current = start # 初始化当前值，用于记录状态
        self.end = end      # 记录终点值

    def __iter__(self):
        # __iter__ 方法只需返回迭代器对象本身
        return self

    def __next__(self):
        # __next__ 方法负责返回下一个值，并更新状态
        if self.current >= self.end:
            # 当没有更多元素时，必须抛出 StopIteration 异常来终止迭代[5](@ref)
            raise StopIteration
        else:
            value = self.current
            self.current += 1 # 更新状态，指向下一个元素
            return value

# 使用自定义迭代器
my_iterator = MyRangeIterator(1, 4) # 创建一个生成 1, 2, 3 的迭代器
print("手动调用 next():")
print(next(my_iterator)) # 输出: 1 (next() 会调用 __next__ 方法)
print(next(my_iterator)) # 输出: 2
print(next(my_iterator)) # 输出: 3
# print(next(my_iterator)) # 如果取消注释，会抛出 StopIteration 异常

# 更常见的用法：用于 for 循环（for 循环会自动处理 StopIteration）
print("\n用于 for 循环:")
for num in MyRangeIterator(1, 4):
    print(num) # 输出: 1, 2, 3
```

### 从可迭代对象获取迭代器

列表这类可迭代对象 (Iterable) 本身不是迭代器 (Iterator)，但可以通过 iter()函数转换为迭代器。

```python
# 列表是可迭代对象，但不是迭代器
my_list = [1, 2, 3, 4, 5]
print(hasattr(my_list, '__iter__'))  # True，是可迭代对象
print(hasattr(my_list, '__next__'))  # False，不是迭代器本身

# 使用iter()函数从列表获取迭代器
list_iterator = iter(my_list)

# 现在可以使用next()遍历
print(next(list_iterator))  # 输出: 1
print(next(list_iterator))  # 输出: 2

# 也可以用于for循环（for循环内部会自动处理迭代器）
for num in list_iterator:
    print(num)  # 输出: 3, 4, 5（继续从之前的位置）
```

元组、字符串、字典、集合等，都可以通过 `iter()` 转换为迭代器。

## 生成器：更简洁的迭代器实现

### 理解生成器

生成器是一种特殊的迭代器，使用yield关键字实现，自动满足迭代器协议。

```python
# 用生成器函数实现类似的MyRange功能
def my_range_generator(start, end):
    current = start
    while current < end:
        yield current  # 使用yield而不是return
        current += 1

# 使用生成器
gen = my_range_generator(1, 4)
print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3
# print(next(gen))  # 再次调用会抛出StopIteration

# 生成器可以直接用于for循环
for num in my_range_generator(1, 4):
    print(num)  # 输出: 1, 2, 3
```

### 生成器表达式

生成器表达式类似列表推导式，但使用圆括号并返回生成器对象。

```python
# 列表推导式：立即计算所有值
list_comp = [x * 2 for x in range(5)]  # [0, 2, 4, 6, 8]

# 生成器表达式：惰性计算，按需生成值
gen_exp = (x * 2 for x in range(5))
print(next(gen_exp))  # 输出: 0
print(next(gen_exp))  # 输出: 2

# 内存占用对比（对于大数据集尤其重要）
import sys
print(sys.getsizeof([x for x in range(1000000)]))  # 约8.4MB
print(sys.getsizeof((x for x in range(1000000))))  # 约120字节
```

### 迭代器 vs 生成器

迭代器和生成器都是惰性计算，内存消耗低，两者都是一次性使用，遍历完之后不能二次遍历，但是相比之下生成器由于函数可以重新调用，使用起来要方便一些。

## 迭代器高级用法

Python标准库提供了强大的itertools模块，包含许多有用的迭代器函数。

```python
import itertools

# 无限迭代器示例
# 1. 无限计数器
counter = itertools.count(start=10, step=2)
print(next(counter))  # 10
print(next(counter))  # 12

# 2. 循环迭代
cycler = itertools.cycle(['A', 'B', 'C'])
print(next(cycler))  # 'A'
print(next(cycler))  # 'B'

# 3. 有限重复
repeater = itertools.repeat('Hello', times=3)
print(next(repeater))  # 'Hello'
print(next(repeater))  # 'Hello'

# 组合迭代器
# 1. 链式迭代：连接多个可迭代对象
chained = itertools.chain([1, 2], ['a', 'b'], [3, 4])
print(list(chained))  # [1, 2, 'a', 'b', 3, 4]

# 2. 切片迭代：对迭代器进行切片
sliced = itertools.islice(itertools.count(), 5, 10, 2)
print(list(sliced))  # [5, 7, 9]
```

生成器不仅可以从外部获取值，还可以通过send()方法接收外部传入的值。

```python
def interactive_generator():
    """一个可以交互的生成器示例"""
    value = yield "开始"  # 第一次yield
    while True:
        value = yield f'收到: {value}'  # 后续yield并接收值

# 使用示例
gen = interactive_generator()
print(next(gen))        # 输出: "开始"（初始启动）
print(gen.send(42))     # 输出: "收到: 42"
print(gen.send('你好'))  # 输出: "收到: 你好"
```

## 函数

### 函数的多种传参方式

Python​ 的函数声明里没有类型，没有返回值，看惯了静态类型语言（比如 Golang）再看 Python 函数总觉得少了点什么。动态类型语言的类型在运行时确定。

```python
def demonstrate_arguments(a, b=2, *args, **kwargs):
    """
    演示多种参数传递方式:
    - a: 位置参数 (必需)
    - b: 默认参数 (可选，默认为2)
    - *args: 可变位置参数 (接收元组)
    - **kwargs: 可变关键字参数 (接收字典)
    """
    print(f"位置参数 a: {a}")
    print(f"默认参数 b: {b}")
    if args:
        print(f"可变位置参数 args: {args}")
    if kwargs:
        print(f"可变关键字参数 kwargs: {kwargs}")
    print("-" * 20)

# 1. 只传递必需的位置参数
demonstrate_arguments(1) # a=1, b使用默认值2

# 2. 传递位置参数和覆盖默认参数
demonstrate_arguments(1, 3) # a=1, b=3

# 3. 使用关键字参数指定，顺序可调整
demonstrate_arguments(b=4, a=2) # a=2, b=4

# 4. 传递额外的位置参数和关键字参数
demonstrate_arguments(1, 2, 3, 4, name="Alice", age=25)
# a=1, b=2, args=(3, 4), kwargs={'name': 'Alice', 'age': 25}
```

### 通过函数实现装饰器

装饰器（Decorator）可以在不修改原函数代码的情况下，为其添加新功能。

```python
import time
from functools import wraps

# 1. 一个简单的计时装饰器
def timing_decorator(func):
    """记录函数运行时间的装饰器"""
    @wraps(func)  # 使用wraps保留原函数的元信息（如函数名）；反之装饰后的函数 name 会变成 wrapper
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs) # 执行原函数
        end_time = time.time()
        print(f"函数 {func.__name__} 运行耗时: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

# 2. 一个带参数的装饰器（创建需要不同权限的装饰器）
def requires_permission(permission_level):
    """检查用户权限的装饰器工厂函数"""
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.get('permission', 0) >= permission_level:  # 字典的 get 用法，0 是默认值
                return func(user, *args, **kwargs)
            else:
                print(f"权限不足！需要 {permission_level} 级权限，当前为 {user.get('permission', 0)} 级。")
                return None
        return wrapper
    return decorator

# 使用装饰器
@timing_decorator
@requires_permission(permission_level=2) # 应用带参数的装饰器
def process_data(user, data_size):
    """模拟一个需要一定权限且耗时的数据处理函数"""
    print(f"用户 {user['name']} 开始处理 {data_size} MB 数据...")
    time.sleep(1) # 模拟耗时操作
    return f"已处理 {data_size} MB 数据"

# 调用被装饰的函数
admin_user = {'name': 'Alice', 'permission': 2}
result = process_data(admin_user, 100)
print(f"结果: {result}")
```

### 函数里的类型注解

类型注解为函数参数和返回值添加预期的类型说明，提高代码可读性和可维护性，并方便IDE进行智能提示和静态类型检查。类型注解本身不会在运行时影响程序的执行或进行强制类型检查。

```python
from typing import List, Dict, Tuple, Optional, Union

def process_user_data(
    user_id: int, # 参数 user_id 应为整数类型
    names: List[str], # 参数 names 应为字符串列表
    metadata: Optional[Dict[str, Union[str, int]]] = None # 参数 metadata 是可选的字典，其值为字符串或整数，默认为None
) -> Tuple[bool, str]:
    """
    处理用户数据的功能示例

    参数:
        user_id: 用户ID，整数类型
        names: 用户可能的名字列表，字符串列表类型
        metadata: 用户元数据，可选的字典型参数，默认为None

    返回:
        一个元组，包含一个布尔值（成功与否）和一个字符串（消息或错误信息）
    """

    if not isinstance(user_id, int) or user_id <= 0:
        return False, "无效的用户ID"

    if not names or not all(isinstance(name, str) for name in names):
        return False, "名字列表必须包含非空字符串"

    print(f"处理用户 {user_id}: 主要名字 - {names[0]}")
    
    if metadata:
        print(f"元数据: {metadata}")

    return True, "处理成功"


# 调用示例
success, message = process_user_data(
    user_id=123,
    names=["Alice", "Alicia"],
    metadata={"age": 30, "role": "admin"}
)

print(f"执行结果: {success}, 信息: {message}")

# 静态类型检查工具（如mypy）可能会对下面的调用提出警告
# result = process_user_data("not_a_number", []) # 参数类型不匹配
# result = process_user_data(123, [1, 2]) # 列表元素类型不匹配
```

## 类

### 类的基本结构和定义

```python
class Car:
    """一个模拟汽车的类"""
    
    # 类属性 (所有实例共享)
    wheels = 4  # 所有汽车通常都有4个轮子

    def __init__(self, brand, model, year):
        """构造器方法，当创建类的新实例时自动调用"""
        # 实例属性 (每个实例独有)
        self.brand = brand    # 品牌
        self.model = model    # 型号
        self.year = year      # 年份
        self.mileage = 0      # 里程数，初始化为0
        self.is_running = False  # 引擎状态，初始化为False

    def start_engine(self):
        """实例方法：启动引擎"""
        if not self.is_running:
            self.is_running = True
            print(f"{self.brand} {self.model}的引擎已启动。")
        else:
            print("引擎已经在运行中。")

    def stop_engine(self):
        """实例方法：停止引擎"""
        if self.is_running:
            self.is_running = False
            print(f"{self.brand} {self.model}的引擎已停止。")
        else:
            print("引擎已经处于停止状态。")

    def drive(self, distance):
        """实例方法：模拟驾驶汽车，增加里程数"""
        if self.is_running:
            self.mileage += distance
            print(f"{self.brand} {self.model}行驶了{distance}公里。")
        else:
            print("请先启动引擎！")

    def get_info(self):
        """实例方法：返回汽车的基本信息"""
        return (f"这是一辆{self.year}年的{self.brand} {self.model}，"
                f"已行驶{self.mileage}公里。")

    def honk(self):
        """实例方法：鸣笛"""
        print(f"{self.brand} {self.model}: 嘀嘀！")

# 实例化Car类，创建两个汽车对象
my_car = Car("Toyota", "Corolla", 2022)
your_car = Car("Tesla", "Model 3", 2023)

# 访问属性
print(f"我的车是{my_car.brand} {my_car.model}。")  # 输出: 我的车是Tesla。
print(f"你的车有{your_car.wheels}个轮子。")         # 输出: 你的车有4个轮子。(访问类属性)
print(f"出厂年份: {my_car.year}")                  # 输出: 出厂年份: 2022

# 调用方法
my_car.start_engine()  # 输出: Tesla的引擎已启动。
my_car.drive(50)       # 输出: Tesla行驶了50公里。
my_car.drive(30)       # 输出: Tesla行驶了30公里。
print(my_car.get_info()) # 输出: 这是一辆2022年的Tesla，已行驶80公里。
my_car.honk()          # 输出: Tesla: 嘀嘀！
my_car.stop_engine()   # 输出: Tesla的引擎已停止。

# 每个对象的状态是独立的
print(f"My car mileage: {my_car.mileage}")    # 输出: My car mileage: 80
print(f"Your car mileage: {your_car.mileage}") # 输出: Your car mileage: 0 (你的车还没开过)

# 修改属性
my_car.mileage = 100  # 直接修改属性
print(f"里程表被调整了: {my_car.mileage}公里") # 输出: 里程表被调整了: 100公里
```

### 类的继承

```python
class Vehicle:
    """交通工具基类"""
    def __init__(self, vehicle_type):
        self.vehicle_type = vehicle_type # 所有交通工具都有类型

    def start(self):
        print(f"{self.vehicle_type}已启动。")

    def stop(self):
        print(f"{self.vehicle_type}已停止。")


class Car(Vehicle): # Car 继承自 Vehicle
    """汽车类，继承自Vehicle"""
    def __init__(self, brand, model):
        # 调用父类Vehicle的__init__方法，将类型固定为"汽车"
        super().__init__("汽车")
        self.brand = brand
        self.model = model

    def accelerate(self):
        print(f"{self.brand} {self.model}正在加速。")

    def get_info(self):
        return f"这是一辆{self.brand} {self.model}。"


class Tesla(Car): # Tesla 继承自 Car
    """Tesla电动车类，继承自Car"""
    def __init__(self, model, battery_capacity):
        # 调用父类Car的__init__方法，品牌固定为"Tesla"
        super().__init__("Tesla", model)
        self.battery_capacity = battery_capacity # Tesla特有的属性：电池容量

    # 方法重写 (Override): Tesla的加速行为与普通汽车不同
    def accelerate(self):
        print(f"Tesla {self.model}正在无声地电驱加速，续航{self.battery_capacity}kWh。")

    # Tesla特有的方法
    def autopilot(self):
        print(f"Tesla {self.model}已开启自动驾驶辅助。")


# 使用继承链
my_tesla = Tesla("Model S", 100)
my_tesla.start()        # 继承自Vehicle的方法: 输出: 汽车已启动。
print(my_tesla.get_info()) # 继承自Car的方法: 输出: 这是一辆Tesla Model S。
my_tesla.accelerate()   # 调用重写后的方法: 输出: Tesla Model S正在无声地电驱加速，续航100kWh。
my_tesla.autopilot()    # 调用Tesla特有的方法: 输出: Tesla Model S已开启自动驾驶辅助。
my_tesla.stop()         # 继承自Vehicle的方法: 输出: 汽车已停止。
```
