0.6 模块与包管理
概述
你已经熟悉 ES6 的 import / export、CommonJS 的 require,以及 npm 的安装、锁文件、发布流程。Python 的模块系统语法相似但语义有差异,包管理用 pip/uv、pyproject.toml、uv.lock。这一章把"导入语法"和"依赖管理"两块都对齐清楚。
JS/TS ↔ Python 对照表
依赖管理对照
核心概念
1. import vs import / require
// JS:ES6
import _ from "lodash";
import { cloneDeep } from "lodash";
import * as utils from "./utils";
// JS:CommonJS
const fs = require("fs");
# Python
import lodash # 导入整个模块,用 lodash.xxx 访问
from lodash import cloneDeep # 导入具体名字
import utils # 导入本地模块 utils.py
Python 没有
export关键字:模块顶层定义的所有名字默认都可供导入(除了下划线开头的被视为内部)。import x类似import * as x,from x import y类似import { y } from 'x'。
2. from x import y vs import { y } from ‘x’
// JS
import { useState, useEffect } from "react";
# Python
from react import useState, useEffect
语法几乎一一对应:from <模块> import <名字> ↔ import { <名字> } from '<模块>'。
3. init.py:包标识
JS 里目录要成为包,需要有 package.json(或 index.js 约定)。Python 里,一个目录要成为包(package),需要包含一个 __init__.py 文件(Python 3.3+ 可以省略,但显式写更清晰)。
my_project/
├── package.json → my_project/
│ ├── __init__.py # 标识这是包
├── src/
│ ├── index.js
│ └── utils.js
# 目录结构
my_project/
├── __init__.py # 包标识(可为空)
├── models.py
└── utils.py
# 导入
from my_project import utils
from my_project.models import User
__init__.py在包被导入时执行,可以放初始化代码,也可以为空。相对导入用点号:
from .utils import helper(当前包)、from ..sibling import x(上级包)。
4. pip/uv install vs npm install
# npm
npm install # 按 package.json 装全部依赖
npm install axios # 添加一个依赖
# uv(现代推荐)
uv sync # 按 pyproject.toml 装全部依赖 + 建虚拟环境
uv add requests # 添加一个依赖并更新锁文件
uv add会自动更新pyproject.toml的dependencies和uv.lock,一步到位(对应npm install <pkg> --save)。uv add --dev pytest加开发依赖。
5. 依赖锁定 uv.lock vs package-lock.json
两者理念一致:锁文件记录精确版本 + 依赖树哈希,保证团队环境可复现。
package-lock.json→uv.lock(或poetry.lock,或老式的requirements.txt)。requirements.txt是历史遗留的"扁平版本清单",不锁传递依赖的完整树;uv.lock/poetry.lock更严格(对应package-lock.json)。
uv lock # 生成/更新 uv.lock(类似 npm install --package-lock-only)
uv sync # 按锁文件安装(类似 npm ci)
6. 虚拟环境隔离原理
你已经理解 node_modules/ 让每个项目的依赖互相隔离。Python 的隔离更彻底:
npm把依赖装到项目内的node_modules/,靠PATH找到可执行文件。Python 的 venv 会创建一个独立的解释器副本 + 独立的
site-packages/目录,激活后python和pip都指向这个环境,依赖也装到这里。
# Node
project/
└── node_modules/
└── axios/
# Python(uv 默认放 .venv)
project/
├── .venv/
│ ├── Scripts/python.exe # 独立解释器
│ └── Lib/site-packages/ # 独立依赖目录
│ └── requests/
7. 发布到 PyPI vs npm publish
# npm
npm login
npm publish
# uv / Python
uv build # 打包成 wheel/sdist
uv publish # 发布到 PyPI
PyPI(pypi.org)就是 Python 的 npm registry。
包名规范:全小写 + 短横线(
my-tool),导入名用下划线(my_tool)。
8. 私有源
# npm:.npmrc
# registry=https://registry.npmmirror.com/
# uv:命令行或 uv.toml
uv add --index https://pypi.org/simple my-pkg
# 或用环境变量 UV_INDEX_URL / 配置文件 uv.toml
代码示例
本地多模块项目结构对比
// utils.js
export function add(a, b) { return a + b; }
export const VERSION = "1.0";
// main.js
import { add, VERSION } from "./utils";
console.log(add(1, 2));
# utils.py
def add(a, b):
return a + b
VERSION = "1.0"
# main.py
from utils import add, VERSION
print(add(1, 2))
入口守卫对比
// main.js
if (require.main === module) {
run();
}
# main.py
def run():
print("运行中")
if __name__ == "__main__":
run()
动态导入对比
// JS
const mod = await import("./config");
# Python
import importlib
mod = importlib.import_module("config")
最佳实践
用
uv init/uv add管理项目,锁文件uv.lock提交到版本库。导入具体名字用
from x import y,导入整个模块用import x,避免from x import *(命名污染,类似import * as的坏味道)。每个包目录都放一个
__init__.py(哪怕为空),意图明确。包内模块间用相对导入
from .foo import bar,别用绝对导入(重构时路径会断)。依赖装进虚拟环境,命令行工具用
uv tool install。本地开发私有包用
uv add --path ../mylib(类似npm link/file:依赖)。
前端开发者常见陷阱
以为有
export关键字:Python 不需要,模块顶层名字自动可导入。想隐藏就加下划线前缀_private。import x和from x import y搞混:import os之后要用os.path;from os import path之后才能直接用path。模块名和变量名冲突:文件叫
json.py会遮蔽标准库的json,导致 import 出错(类似 JS 里覆盖全局)。相对导入报错:直接运行一个包内模块(
python pkg/mod.py)时相对导入会失败,因为__name__不是包路径,需从项目根用-m运行(python -m pkg.mod)。忘了虚拟环境:直接
pip install装到全局,换项目后依赖混乱。requirements.txt和pyproject.toml二选一混淆:新项目用pyproject.toml+uv.lock,requirements.txt是旧方案。循环依赖:A 导入 B、B 导入 A,运行时
AttributeError/ImportError,和 JS 的循环依赖一样要重构。包目录缺少
__init__.py:某些工具/老代码会报找不到模块,显式加一个空文件最稳妥。sys.path不懂:import 时 Python 从sys.path找模块,脚本所在目录默认在其中,但项目根不一定。这就是为什么运行路径不同会导致 import 失败。把 npm 的
^~版本语法照搬:Python 用>=、==、~=(兼容版本),语义不同。
总结
import x≈import * as x,from x import y≈import { y } from 'x',无 export 关键字。包 = 含
__init__.py的目录,相对导入用.和..。uv 是现代的包管理器:
uv add/uv sync/uv remove对应 npm 的 install 流程。uv.lock≈package-lock.json,pyproject.toml≈package.json,PyPI ≈ npm registry。虚拟环境比
node_modules隔离更彻底,连解释器一起隔离;新项目务必用uv init起步。
0.6-模块与包管理
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法