启动文件与配置
Python 的交互式解释器在启动时会做一系列初始化:设置 sys.path、加载 site 模块、读取环境变量、执行 PYTHONSTARTUP 指定的脚本。理解这套启动顺序,就能把提示符、补全、历史、常用导入等个性化配置放在正确的位置。
前置知识
阅读本节前,建议先了解:颜色高亮与自动补全
基础概念
Python 启动时涉及多个「配置文件」与「环境变量」,职责各不相同:
| 名称 | 是否只影响交互模式 | 作用 |
|---|---|---|
PYTHONSTARTUP | 是 | 指向启动脚本,交互模式启动时执行 |
sitecustomize.py | 否 | site 模块自动导入,影响所有 Python 进程 |
usercustomize.py | 否 | 类似 sitecustomize,面向用户级自定义 |
site.py / .pth 文件 | 否 | 路径扩展与 site 初始化 |
PYTHONPATH | 否 | 追加模块搜索路径 |
PYTHONHISTORY | 是 | 自定义历史文件位置 |
PYTHONCOERCECLOCALE 等 | 否 | 解释器行为微调 |
关键区别在于作用范围:
PYTHONSTARTUP只在交互模式下执行,脚本运行时不执行。因此可以放心放交互专用的便利设置。sitecustomize.py/usercustomize.py影响所有 Python 进程,包括你运行的每个脚本和工具,改动需谨慎。.pth文件在site初始化时逐行处理,常用于把额外目录加入sys.path。
Python 的启动顺序(简化):
1. 解释器初始化,处理命令行参数(-c、-m、-i、-E、-S 等)
2. 初始化 sys.path(含脚本目录、PYTHONPATH、标准库、site-packages)
3. 若未指定 -S,导入 site 模块
├─ 处理 site-packages 下的 .pth 文件
├─ 尝试导入 sitecustomize
└─ 尝试导入 usercustomize
4. 若处于交互模式且设置了 PYTHONSTARTUP,执行该脚本
5. 进入 REPL 循环或执行脚本因此,PYTHONSTARTUP 是最后执行的,它的设置会覆盖前面阶段的默认值。
语法与用法
设置 PYTHONSTARTUP:
# Bash / Zsh:写入 ~/.bashrc 或 ~/.zshrc
export PYTHONSTARTUP="$HOME/.pythonrc.py"
# Fish
set -Ux PYTHONSTARTUP "$HOME/.pythonrc.py"
# 验证环境变量已生效
echo "$PYTHONSTARTUP"创建一个最小的启动脚本:
# ~/.pythonrc.py
"""Python 交互式解释器的启动脚本。"""
import sys
print(f"Python {sys.version.split()[0]} 交互会话已就绪")启动验证:
python
# 输出:Python 3.13.1 交互会话已就绪
# 随后进入 >>> 提示符确认启动脚本确实只在交互模式执行:
# 交互模式:会打印提示
python
# Python 3.13.1 交互会话已就绪
# 脚本模式:不会打印
echo 'print("脚本运行")' > demo.py
python demo.py
# 脚本运行sitecustomize.py 的用法不同,它需要放在 sys.path 能找到的位置(通常在 site-packages):
# sitecustomize.py(放在 site-packages 目录)
import sys
# 该文件会在所有 Python 进程启动时被导入
if sys.version_info < (3, 10):
raise RuntimeError("本环境要求 Python 3.10 及以上")用 .pth 文件扩展路径:
# mypaths.pth(放在 site-packages 目录,每行一个路径)
/Users/demo/work/libs
/Users/demo/work/tools验证 .pth 是否生效:
python -c "import sys; print([p for p in sys.path if 'libs' in p])"
# ['/Users/demo/work/libs']详细说明
PYTHONSTARTUP 的执行时机与限制
该变量指向的脚本仅在交互模式下执行,具体条件:
- 解释器启动后进入交互模式(无脚本参数,或使用
-i); - 环境变量
PYTHONSTARTUP指向一个存在的文件; - 未设置
-E(-E会忽略所有PYTHON*环境变量)。
若脚本本身抛异常,解释器会打印回溯但继续进入 REPL,不会退出。这一点很友好——配置写错不会导致无法启动。
# 故意写错以观察行为
undefined_name # 启动时会打印 NameError,但仍能进入 REPL$ python
Traceback (most recent call last):
File "/Users/demo/.pythonrc.py", line 2, in <module>
undefined_name
NameError: name 'undefined_name' is not defined
>>>启动脚本中可用的上下文
PYTHONSTARTUP 脚本在一个特殊命名空间中执行:它不是 __main__,但其中的全局变量会成为交互会话的初始命名空间。因此:
# ~/.pythonrc.py
# 这些名字在进入 REPL 后可直接使用
import datetime
import json
from pathlib import Path
today = datetime.date.today
home = Path.home()>>> today()
datetime.date(2026, 10, 8)
>>> home
PosixPath('/Users/demo')
>>> json.dumps({"ok": True})
'{"ok": true}'这使启动脚本成为「预导入常用模块」的理想位置。但要注意:不要在这里导入过重的库(如 pandas、torch),否则每次启动 REPL 都会变慢。
__file__ 在该脚本中指向 PYTHONSTARTUP 的路径,可用于定位同目录的辅助文件。
自定义提示符
sys.ps1 与 sys.ps2 只在交互模式下存在,可在启动脚本中修改:
# ~/.pythonrc.py
import sys
# 显示当前路径与 Python 版本
sys.ps1 = "py> "
sys.ps2 = "..> "用颜色装饰提示符(需要终端支持 ANSI):
# ~/.pythonrc.py
import sys
GREEN = "\001\033[32m\002"
RESET = "\001\033[0m\002"
# 注意:\001 与 \002 是 readline 的「不可见字符」标记,
# 避免计算光标位置时把转义序列算进宽度
sys.ps1 = f"{GREEN}>>> {RESET}"
sys.ps2 = f"{GREEN}... {RESET}"若用 3.13 的新 REPL,提示符着色已内置,通常无需手动添加转义序列。
sitecustomize 与 usercustomize 的分工
| 文件 | 位置 | 典型用途 |
|---|---|---|
sitecustomize.py | site-packages,由管理员或环境提供 | 全环境策略,如版本校验、编码设置 |
usercustomize.py | 用户级 site-packages(如 ~/.local/lib) | 个人偏好,跨环境生效 |
usercustomize 只在未设置 -s(禁用用户站点目录)时导入。二者的差异在于作用范围与权限:前者是环境级,后者是用户级。
一个实用的 sitecustomize 示例——为所有进程统一 UTF-8 与警告策略:
# sitecustomize.py
import sys
import warnings
# 让所有进程默认以 UTF-8 读取源码与标准流
if hasattr(sys, "set_int_max_str_digits"):
# 提高大整数转字符串的上限(按需设置,注意安全影响)
sys.set_int_max_str_digits(100000)
# 统一警告过滤策略
warnings.filterwarnings("default", category=DeprecationWarning)需要强调:sitecustomize 会影响所有 Python 进程,包括 pip、构建工具等。改动前应确认不会破坏工具链。
常用的启动脚本模板
一份实用的启动脚本通常包含四块内容:
- 预导入常用模块;
- 补全与历史配置;
- 提示符与输出美化;
- 便利函数与快捷键。
下面给出一个可直接使用的完整模板。
# ~/.pythonrc.py
"""Python 交互式解释器的启动配置(完整模板)。"""
from __future__ import annotations
import atexit
import os
import readline
import rlcompleter
import sys
from pathlib import Path
# ---------- 1. 预导入常用模块 ----------
import datetime
import itertools
import json
import math
import random
import re
# ---------- 2. 补全 ----------
_completer = rlcompleter.Completer()
def _path_aware(text: str, state: int) -> str | None:
"""含路径分隔符时补全文件名,否则交给 rlcompleter。"""
if os.sep in text:
directory, prefix = os.path.split(text)
base = directory or "."
try:
matches = [n for n in sorted(os.listdir(base)) if n.startswith(prefix)]
except OSError:
return None
if state < len(matches):
return os.path.join(directory, matches[state]) if directory else matches[state]
return None
return _completer.complete(text, state)
readline.set_completer(_path_aware)
readline.parse_and_bind("tab: complete")
readline.set_completer_delims(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?")
# ---------- 3. 历史 ----------
HISTORY = Path(os.environ.get("PYTHON_HISTORY", Path.home() / ".python_history"))
if HISTORY.exists():
readline.read_history_file(HISTORY)
readline.set_history_length(2000)
atexit.register(readline.write_history_file, HISTORY)
# ---------- 4. 便利工具 ----------
def pwd() -> str:
"""返回当前工作目录,方便在 REPL 中查看。"""
return os.getcwd()
def reload(module: object) -> None:
"""重新导入模块,便于开发时快速验证改动。"""
import importlib
importlib.reload(module)
print(f"Python {sys.version.split()[0]} | Tab 补全、历史已启用 | 试试 pwd()")保存为 ~/.pythonrc.py 并在 Shell 中导出 PYTHONSTARTUP 即可生效。
环境变量与命令行开关的交互
-E 会忽略所有 PYTHON* 环境变量,因此 PYTHONSTARTUP 也会失效:
# 正常:会执行启动脚本
python
# 忽略环境变量:启动脚本不执行
python -E
# 跳过 site 模块:sitecustomize/usercustomize 不导入
python -S
# 先运行脚本再进入交互(会执行启动脚本)
python -i script.py| 开关 | 影响 |
|---|---|
-E | 忽略 PYTHON* 环境变量(含 PYTHONSTARTUP、PYTHONPATH、PYTHONHISTORY) |
-S | 不导入 site,sitecustomize/usercustomize/.pth 全部跳过 |
-s | 不把用户级 site-packages 加入 sys.path |
-I | 隔离模式,等价于 -E -s 再加其他限制 |
-i | 执行脚本后进入交互模式(此时执行启动脚本) |
排查配置问题时,python -E 与 python -S 是最快的定位手段:前者禁用所有环境变量,后者禁用站点初始化。
让配置在虚拟环境与容器中同样生效
PYTHONSTARTUP 是用户级设置,虚拟环境不会覆盖它,因此配置在所有 venv 中都有效。但要注意:
- 启动脚本中不要硬编码某个虚拟环境的路径;
- 若启动了
-I或-E(如某些工具内部使用),配置不会生效,这是正常行为; - 容器中若未设置
PYTHONSTARTUP,可写进镜像的 Shell 初始化或ENV指令; - 共享服务器上应把个人配置放在用户目录,不要写进系统级
sitecustomize。
与 -i 模式的配合
python -i script.py 会先执行脚本,再进入交互模式,此时启动脚本会被执行。因此用 -i 调试时也能享受补全与历史。但顺序上,启动脚本先执行,脚本后执行,脚本中的定义会覆盖启动脚本的同名变量。
python -i explore.py
# 先打印启动脚本的提示,再执行 explore.py,最后进入 REPL实战示例
示例一:为数据探索准备启动环境
做数据分析时,常需要反复导入若干库。把它们放进启动脚本,可省去重复输入。
# ~/.pythonrc_data.py
"""数据探索专用的启动脚本。"""
from __future__ import annotations
import csv
import datetime as dt
import json
import statistics
from pathlib import Path
def load_csv(path: str | Path) -> list[dict[str, str]]:
"""读取 CSV 文件,返回字典列表。"""
with Path(path).open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
def summarize(values: list[float]) -> dict[str, float]:
"""返回一组数值的常用统计量。"""
return {
"count": len(values),
"mean": statistics.fmean(values),
"median": statistics.median(values),
"stdev": statistics.pstdev(values),
}# 使用时切换启动脚本
PYTHONSTARTUP=~/.pythonrc_data.py python>>> rows = load_csv("sales.csv")
>>> summarize([float(r["amount"]) for r in rows])
{'count': 120, 'mean': 218.4, 'median': 199.0, 'stdev': 87.3}启动脚本成为「探索环境的一部分」,比每次手敲导入更可靠。
示例二:用 sitecustomize 统一团队的编码与警告策略
团队希望所有脚本默认使用 UTF-8,并把废弃警告显式暴露出来。这适合放在环境级配置中。
# sitecustomize.py(放在项目虚拟环境的 site-packages 中)
"""环境级启动配置:统一编码与警告策略。"""
import sys
import warnings
# 确保标准输出/错误使用 UTF-8(部分 Windows 环境默认是 GBK)
if sys.stdout.encoding and sys.stdout.encoding.lower() not in {"utf-8", "utf8"}:
try:
sys.stdout.reconfigure(encoding="utf-8") # type: ignore[union-attr]
sys.stderr.reconfigure(encoding="utf-8") # type: ignore[union-attr]
except AttributeError:
pass # 老版本或非文本流,忽略
# 让废弃警告可见,推动代码保持现代写法
warnings.filterwarnings("default", category=DeprecationWarning)把该文件放进虚拟环境的 site-packages,则该环境下的所有进程都会生效。验证:
python -c "import sys; print(sys.stdout.encoding)"
# utf-8注意:sitecustomize 只在 site 初始化时被导入,使用 -S 启动的进程不会加载它。
示例三:用 .pth 文件注入团队公共库
团队有一套内部工具库放在共享目录,希望所有项目都能直接导入。用 .pth 是最轻量的方式。
# team_libs.pth(放在 site-packages 目录)
/opt/team/libs
/opt/team/vendor也可以利用 .pth 的 import 语法执行一行代码:
# enable_team_hooks.pth
import sys; sys.path.insert(0, "/opt/team/libs")不过这行代码只在路径不存在时才应执行,更稳妥的写法是用普通路径行(第一行形式)。验证注入是否生效:
>>> import sys
>>> [p for p in sys.path if "team" in p]
['/opt/team/libs', '/opt/team/vendor']示例四:诊断启动配置是否按预期生效
当配置「看起来没生效」时,用一段脚本逐项核对。
# check_startup.py
from __future__ import annotations
import importlib.util
import os
import sys
def check() -> list[tuple[str, str]]:
"""检查各类启动配置的生效情况。"""
startup = os.environ.get("PYTHONSTARTUP")
rows: list[tuple[str, str]] = [
("PYTHONSTARTUP", startup or "<未设置>"),
("启动脚本存在", str(bool(startup) and os.path.isfile(startup))),
("PYTHONPATH", os.environ.get("PYTHONPATH", "<未设置>")),
("PYTHONHISTORY", os.environ.get("PYTHONHISTORY", "<未设置>")),
("交互模式", str(hasattr(sys, "ps1"))),
("忽略环境变量(-E)", str(sys.flags.ignore_environment == 1)),
("跳过 site(-S)", str(sys.flags.no_site == 1)),
("sitecustomize 可导入", str(importlib.util.find_spec("sitecustomize") is not None)),
("usercustomize 可导入", str(importlib.util.find_spec("usercustomize") is not None)),
]
return rows
if __name__ == "__main__":
for name, value in check():
print(f"{name:24} {value}")重点看两项:交互模式 若为 False,说明当前是脚本模式,启动脚本本就不会执行;忽略环境变量(-E) 若为 True,则所有 PYTHON* 变量都被跳过。
注意事项
PYTHONSTARTUP只在交互模式生效:在脚本里依赖它必然失败,跨模式共享的逻辑应放sitecustomize。- 不要硬编码虚拟环境路径:启动脚本是用户级的,硬编码会污染其他环境。
- 启动脚本异常不会阻止启动:会打印回溯但继续进入 REPL,容易误以为配置生效了,需留意输出。
sitecustomize影响所有进程:写错会影响pip、构建工具等,改动前务必在隔离环境验证。-E会禁用全部PYTHON*变量:排查问题时先用它确认是否与环境变量有关。-S会跳过site、.pth、usercustomize:诊断站点配置问题时用它对照。- 不要在启动脚本里做重活:导入大型库或执行网络请求会显著拖慢每次启动。
sys.ps1只在交互模式存在:脚本中访问会抛AttributeError,需先判断。.pth文件的一行一路径:空行与#开头的行会被忽略,路径中不要带引号。
常见问题
Q:设置了 PYTHONSTARTUP 但没生效? 依次检查:文件路径是否正确、Shell 是否重新加载配置、是否用了 -E、当前是否交互模式。
Q:PYTHONSTARTUP 与 PYTHONPATH 有什么区别? 前者是「要执行的脚本」,后者是「模块搜索路径」。一个改变行为,一个改变导入范围。
Q:启动脚本会随虚拟环境变化吗? 不会。PYTHONSTARTUP 是用户级环境变量,所有 venv 共用。需要环境级配置请用 sitecustomize。
Q:能在启动脚本中自动激活虚拟环境吗? 不推荐。虚拟环境的激活应由 Shell 负责,在启动脚本中修改 sys.path 容易造成混乱。
Q:如何临时禁用启动脚本?PYTHONSTARTUP= python 或 python -E。
Q:usercustomize 和 sitecustomize 冲突吗? 不冲突,二者都会导入。一般约定 sitecustomize 由环境提供,usercustomize 由用户提供。
最佳实践
- 交互专用配置放
~/.pythonrc.py,通过PYTHONSTARTUP加载。 - 环境级、影响所有进程的配置放
sitecustomize.py,并纳入版本控制。 - 启动脚本只做轻量操作:预导入、补全、历史、便利函数。
- 用
-E与-S快速定位配置问题,先排除环境变量再排查代码。 - 用诊断脚本定期核对配置生效情况,尤其在换机器后。
- 不在启动脚本中硬编码路径或做网络访问。
- 把启动脚本纳入 dotfiles 仓库管理,便于在多台机器间同步。
下一节
下一章将进入语言规范与社区:Python 之禅 Zen of Python