unimport 是一个非常有用的 Python 工具,它可以帮助你自动检测和移除未使用的导入(imports),这不仅能保持代码的整洁,还能在一定程度上减少代码体积,提高代码的可读性。

什么是 unimport?
unimport 是一个命令行工具,它通过静态分析你的 Python 代码,找出那些被导入但从未在代码中实际使用的模块、包和名称,它会将这些无用的导入从你的源文件中移除。
你可以把它想象成一个 Python 代码的“减肥”或“清理”工具。
为什么使用 unimport?
使用 unimport 带来几个主要好处:
- 保持代码整洁:删除冗余的导入,让代码看起来更清爽,更容易理解。
- 减少代码体积:虽然单个导入语句的开销很小,但在大型项目中,累积起来也能减少文件的体积。
- 避免命名冲突:有时,你可能会无意中导入一个名称(比如一个函数或变量),但它从未被使用,这可能会在未来的代码中导致难以发现的命名冲突,移除未使用的导入可以避免这类问题。
- 提高代码审查效率:当别人审查你的代码时,他们不必分心于那些无用的导入。
- 集成到开发流程:可以很容易地集成到你的 CI/CD(持续集成/持续部署)流程或 pre-commit hooks(提交前检查)中,确保代码库始终保持干净。
如何安装 unimport
安装 unimport 非常简单,只需要使用 pip:

pip install unimport
如何使用 unimport
unimport 提供了命令行接口,使用起来非常直观。
基本用法
最简单的用法是直接将 Python 文件或目录作为参数传递给它。
示例文件 my_app.py:
# 这是一个示例文件,包含一些未使用的导入
import os
import sys
import json
from datetime import datetime
from typing import List
def get_user_data():
# 假设这里需要用到 os 和 json
user_info = {"name": "Alice", "age": 30}
print(json.dumps(user_info))
# os.getcwd() # 假设这行被注释掉了,os 就没有被使用
return user_info
def main():
# sys 和 datetime 在这里被使用了
print("Hello, World!")
print(f"Current time: {datetime.now()}")
print(sys.version)
if __name__ == "__main__":
main()
运行 unimport 命令:

# 对单个文件进行检查 unimport my_app.py # 对整个目录进行检查 unimport my_project/
输出结果:
当你运行 unimport my_app.py 时,它会输出类似下面的信息,告诉你哪些导入是未使用的:
Scanning my_app.py...
Unused imports:
- os (line 2)
- typing.List (line 5)
自动修复(修改源文件)
unimport 最强大的功能是它可以直接修改你的源文件,自动移除未使用的导入。
使用 --remove 或 -r 标志:
unimport --remove my_app.py
运行后,my_app.py 文件的内容会自动更新为:
# my_app.py (修改后)
import sys
import json
from datetime import datetime
def get_user_data():
user_info = {"name": "Alice", "age": 30}
print(json.dumps(user_info))
return user_info
def main():
print("Hello, World!")
print(f"Current time: {datetime.now()}")
print(sys.version)
if __name__ == "__main__":
main()
可以看到,import os 和 from typing import List 这两行已经被自动移除了。
其他常用选项
--include/-i: 指定要包含的文件或目录的 glob 模式。# 只检查 src 目录下的 .py 文件 unimport --include "src/**/*.py" --remove
--exclude/-e: 指定要排除的文件或目录的 glob 模式。# 检查整个项目,但排除 tests 目录 unimport --exclude "tests/**/*" --remove
--diff/-d: 显示将要进行的更改,但不实际修改文件,这对于在 CI/CD 中进行预检查非常有用。unimport --diff my_app.py # 输出类似 git diff 的内容
--check/-c: 只检查文件,如果发现未使用的导入则返回非零退出码,不修改文件,适合用在 pre-commit hooks 中。unimport --check my_app.py # 如果有未使用的导入,命令会返回 1
高级用法:集成到开发工作流
将 unimport 集成到你的开发工作流中,可以确保代码库始终保持干净。
a) 与 pre-commit 集成
pre-commit 是一个强大的工具,可以在你 git commit 之前运行代码检查。
-
安装 pre-commit:
pip install pre-commit
-
在项目根目录创建
.pre-commit-config.yaml文件:repos: - repo: https://github.com/hakancelik/unimport rev: v0.15.2 # 使用一个稳定的版本号 hooks: - id: unimport args: [--remove, --include, "*.py"] # 默认会移除,检查所有 .py 文件 -
安装 pre-commit hooks:
pre-commit install
每次你尝试 git commit 时,unimport 都会自动运行,如果发现未使用的导入并且 --remove 标志存在,它会直接修改你的文件,如果只想检查而不修改,可以将 args 改为 [--check]。
b) 与 GitHub Actions 集成
你可以在你的 CI/CD 流水线中加入 unimport 检查,确保合并到主分支的代码是干净的。
.github/workflows/lint.yml 示例:
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install unimport
- name: Run unimport check
run: |
# 只检查,不修改,如果发现问题,任务会失败。
unimport --check .
unimport 的工作原理与局限性
工作原理
unimport 主要依赖于 Python 的 ast (Abstract Syntax Tree, 抽象语法树) 模块,它会解析你的 Python 代码,生成一个 AST,它会遍历这个 AST,记录下所有被导入的名称,再与代码中实际使用的名称进行比较,那些在导入列表中但未在使用列表中的名称,就被认为是“未使用的”。
局限性
-
动态导入:
unimport无法检测动态导入,例如通过eval()、exec()或importlib.import_module()进行的导入,因为它是在静态分析阶段,无法预知这些动态调用的结果。# unimport 无法发现这里的 'some_module' module_name = "some_module" __import__(module_name)
-
字符串中的导入:如果模块名称出现在字符串中,
unimport也无法识别。# unimport 无法发现这里的 'requests' config = "import requests"
-
__all__变量:unimport主要关注代码的实际使用,如果一个模块被导入,但其下的某个名称只存在于__all__列表中但从未被实际调用,unimport可能会认为该名称是未使用的,这通常是符合预期的,但如果你的逻辑依赖于__all__,需要小心。 -
类型注解:
unimport能够很好地处理大多数类型注解,但在某些复杂的、嵌套的类型场景下,可能会有误判,它的最新版本一直在改进这方面的支持。
unimport 是一个轻量级、高效且易于使用的 Python 代码清理工具,它能显著提升代码库的质量和可维护性,强烈建议所有 Python 开发者,尤其是在团队协作中,将 unimport 纳入到他们的工具箱中,并集成到开发工作流中。
