Python 3.13 + uv 内联脚本元数据:把依赖写在脚本头部,省心又干净

2026-08-28 0 689

写个小工具的时候,最烦的就是为了用某个库先去 `pip install` 一下,然后又怕污染全局环境,于是手动 `python -m venv venv`,激活,再安装。折腾完一句脚本没写。现在 Python 3.13 正式把内联脚本元数据PEP 723)提上日程,你再配合 uv 这个神器,体验堪比“脚本自动配环境”。

PEP 723 是个什么天降奇兵

简单说,它允许你在 Python 脚本文件开头用 `#` 注释写一段 TOML 格式的元数据。里面主要是声明这个脚本需要哪些第三方包。当你用支持 PEP 723 的工具(比如 uv)去运行这个脚本时,它自动创建虚拟环境、安装依赖、然后执行脚本。整个过程你看到的只是一句 `uv run script.py`,但背后它已经帮你解决掉了依赖隔离。

看一下基本的结构:

加个 python 文件的头部:

    # /// script
    # requires-python = ">=3.12"
    # dependencies = [
    #     "requests>=2.31",
    # ]
    # ///
    

注意 `# /// script` 和 `# ///` 是固定标记,中间的内容必须是合法的 TOML。目前支持的键有 `requires-python` 和 `dependencies`,还有 `include` / `exclude` 之类的,但我们常用前两个。

为什么说它是今日热度?

因为 Python 3.13 在 2024 年 10 月刚发布了正式版,而且配套的 `uv` 工具也在这段时间疯狂迭代。 `uv` 本身是一个超快的 Python 包管理器(Rust 写的),内置了对 PEP 723 的支持。也就是说你不需要等官方在 Python 解释器里实现自动装依赖,用 `uv` 就够了。

我自己的使用体验就俩字:真香。以前写个爬虫脚本,随便用个 `httpx` 都要先建虚拟环境。现在直接在脚本里声明,然后用 `uv run` 跑,依赖自动安装到 uv 管理的该脚本专属缓存环境中。速度还贼快。

实战:做一个“一句话查天气”的小工具

我们来写一个真实的案例。这个脚本会调用 wttr.in 的接口,用 `httpx` 获取天气信息,然后用 `rich` 打印到终端。因为需要第三方包,正好用上内联元数据。

先建文件 `weather.py`,内容如下:

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "httpx>=0.27",
#     "rich>=13.7",
# ]
# ///

import sys
import httpx
from rich import print

def get_weather(city: str) -> str:
    url = f"https://wttr.in/{city}?format=j1"
    r = httpx.get(url, timeout=10)
    r.raise_for_status()
    data = r.json()
    current = data["current_condition"][0]
    desc = current["lang_zh"][0]["value"] if "lang_zh" in current else "未知"
    return f"{city}: {current['temp_C']}°C, {desc}"

if __name__ == "__main__":
    city = sys.argv[1] if len(sys.argv) > 1 else "Beijing"
    print(get_weather(city))
    

注意看文件最上面的元数据:声明了 `pure Python >=3.12`,并依赖 `httpx` 和 `rich`。现在正常手动跑的话,你需要先安装这两个库。但是用 uv 跑就不用了:

uv run weather.py Shanghai
    

第一次运行的时候,uv 会解析 `# /// script` 里的依赖,自动创建一个隔离环境(uv 自己管理,不放在当前目录),安装 `httpx` 和 `rich` 以及它们的传递依赖,然后执行脚本。你会看到类似这样的输出:

Installed 9 packages in 12ms
上海: 18°C, 多云
    

以后再去改动依赖版本,它会增量更新。整个过程几乎是无感的,也不会在你项目文件夹里留下任何 `venv` 垃圾。

进阶:把脚本打包成可直接跑的“命令”

如果你不想每次都用 `uv run weather.py` 这么长,可以在 `dependencies` 后面加上一行脚本入口定义:

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "httpx>=0.27",
#     "rich>=13.7",
# ]
# [tool.uv]
# package = true
# [tool.uv.scripts]
# weather = "weather:main"
# ///
    

不过这有点超前,可能 `[tool.uv.scripts]` 还没有完全稳定。一个更稳妥的做法是直接用 `uv run –script weather.py` 或者 `uv run –script weather.py — args`。

如果你实在想把它变成一个全局命令,可以用 `uv tool install`,它支持指定一个脚本文件或 GitHub 上的脚本。但是对单文件工具来说,直接 `uv run` 就够了。

几个常见疑问

Q: 是不是必须在 Python 3.13 上才能用?不用。PEP 723 只是格式规范,你只要用支持该格式的运行工具即可。uv 支持任何 Python 版本,只要你的系统里有对应的解释器。这里的脚本里 `requires-python = “>=3.12″` 只是告诉 uv 要用哪个 Python 版本,并不是强制要求 3.13。

Q: 这样做会不会隐藏依赖关系,反而让人困惑?正好相反,依赖直接写在脚本头部,任何人拿到脚本一眼就知道需要什么,比 readme 还直观。而且因为打包到脚本自己里边,就不会出现“跑不起来才想起来缺依赖”的情况。

Q: 我写的脚本要给别人用,对方没有 uv 怎么办?至少在 2024 年底,`uv` 还是个单独的二进制,不是 Python 自带。你可以鼓励对方用 `pip install uv` 安装,或者把脚本文本和依赖一并放到一个 requirements 里。但更推荐的是,如果对方也装了 uv,那么运行体验完全无痛。

Q: 这个特性会影响普通 `.py` 文件吗?不会。如果没有 `# /// script` 标记,Python 会把整个文件当作普通注释,所以哪怕你不用 uv 跑,改用 `python weather.py` 也只会忽略这些注释,顶多因为没有安装依赖而报错,不会影响语法。

什么时候不适合这种玩法

当你的脚本依赖很多(比如 pandas、numpy 这种大块头),并且还需要一些编译好的 wheel,那么 uv 也能处理,只是首次安装时间会明显增加。这时候还是建议用传统项目方式,做一个正规的 `pyproject.toml`。另外一个场景是你的代码要在服务器上长期运行,比如每天定时任务,那么直接用 `uv run` 也可能不合适,因为 uv 会自己管理环境,不一定兼容 cron 的环境变量。还是用正式的 virtualenv 更稳。

最后说句实在的

PEP 723 真正落地,加上 uv 的执行效率,已经在改变我写小工具的习惯。以前我们总是为了一小段代码做一堆“仪式感”,现在可以真正做到:一个 `.py` 文件就是一个独立应用。你甚至可以直接把这种脚本放在 GitHub gist 上,别人复制到本地后 `uv run xxx.py` 就能跑。

试试看,你会回来谢谢我的。

Python 3.13 + uv 内联脚本元数据:把依赖写在脚本头部,省心又干净
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 python Python 3.13 + uv 内联脚本元数据:把依赖写在脚本头部,省心又干净 https://www.taomawang.com/server/python/2658.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务