对的,这是一篇学习笔记,和上一篇没啥关系。
此刻,我写不来 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 # 包内的另一个模块
|
各个文件内容如下:
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 # (先注释掉,待会演示)
|
my_package/module_a.py
1
2
3
4
|
print("执行 module_a.py 模块")
def hello_from_a():
return "Hello from Module A!"
|
my_package/module_b.py
1
2
3
4
|
print("执行 module_b.py 模块")
def hello_from_b():
return "Hello from Module B!"
|
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
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
|
激活虚拟环境后,python
和 pip
命令就会使用 .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
|
- 安装依赖,编写代码
接着安装 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
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
里面。
- 退出虚拟环境
需要退出虚拟环境,可以直接执行 deactivate
- 复现环境
现在将依赖已经全部维护到了 requirements.txt 文件里,这个 requirements.txt 会跟着源代码一起上库,但是 .venv 目录显然是不适合丢到代码库的。
当你下载源代码到一个新环境的时候,就可以通过 requirements.txt 里记录依赖清单来安装依赖,将项目跑起来:
1
|
pip install -r requirements.txt
|