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

目录

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

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 文件就是一个模块,模块代码的整体布局如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"""
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 可以导入包、导入模块、可以导入任何定义在模块顶级的名称,包括变量、常量、函数、类等。

假如有这样一个项目:

1
2
3
4
5
6
my_project/
├── main.py           # 主程序,用于演示导入
└── my_package/       # 自定义的包
    ├── __init__.py   # 包的初始化文件
    ├── module_a.py   # 包内的一个模块
    └── module_b.py   # 包内的另一个模块

各个文件内容如下:

  1. my_package/__init__.py​
1
2
3
4
5
6
7
8
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  # (先注释掉,待会演示)
  1. my_package/module_a.py​
1
2
3
4
print("执行 module_a.py 模块")

def hello_from_a():
    return "Hello from Module A!"
  1. my_package/module_b.py
1
2
3
4
print("执行 module_b.py 模块")

def hello_from_b():
    return "Hello from Module B!"
  1. main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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 执行结束 ---")

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--- 开始执行 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 环境。

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

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

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

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

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

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

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

  1. 创建虚拟环境
1
2
3
4
5
mkdir py-projects
cd py-projects
mkdir my-web-crawler
cd my-web-crawler
python -m venv .venv

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

1
2
3
4
5
.venv
├── bin
├── include
├── lib
└── pyvenv.cf

激活虚拟环境后,pythonpip 命令就会使用 .venv 里的:

1
source .venv/bin/activate

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

1
2
3
4
5
6
# 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
  1. 安装依赖,编写代码

接着安装 requests 包:

1
pip install requests

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

新建一个 main.py 文件,写入代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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

1
pip install beautifulsoup4
  1. 执行代码
1
2
# python main.py
The title of 'https://www.danielhu.cn/' is: 胡涛的个人网站 | Seven Coffee Cups

生成依赖清单:

1
pip freeze > requirements.txt

此时 requirements.txt 内容如下:

1
2
3
4
5
6
7
8
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 里面。

  1. 退出虚拟环境

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

  1. 复现环境

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

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

1
pip install -r requirements.txt

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

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

1
2
3
4
5
6
7
mkdir my-first-flask-app
cd my-first-flask-app

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

pip install Flask

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 导入模块:从 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)

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!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() 分别迭代键、值或 (键, 值) 对 动态反映字典的变化

见下例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 综合演示 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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 基础:快速生成列表
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']
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 快速创建字典:数字映射到它的平方
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}
1
2
3
4
5
6
7
8
9
# 从列表中创建去重后的集合
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) 是用于惰性遍历数据集合的对象,它一次只产生一个元素,以实现内存节省。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建一个自定义迭代器,生成一个有限的数字序列
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()函数转换为迭代器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 列表是可迭代对象,但不是迭代器
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关键字实现,自动满足迭代器协议。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 用生成器函数实现类似的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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 列表推导式:立即计算所有值
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字节

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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()方法接收外部传入的值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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 函数总觉得少了点什么。动态类型语言的类型在运行时确定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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)可以在不修改原函数代码的情况下,为其添加新功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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进行智能提示和静态类型检查。类型注解本身不会在运行时影响程序的执行或进行强制类型检查。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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]) # 列表元素类型不匹配
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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公里
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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的方法: 输出: 汽车已停止。