这不过是 Confuse-a-Cat 日常工作的一部分。
——Monty Python
预览
Python 一直在演进,以跟上不断变化的技术世界。本章会讨论一些适用于上一章所提问题的 Python 特性,以及少量额外内容:
工具
API 和服务
变量和类型提示
数据结构
Web 框架
工具
每一种计算机语言都包含以下内容:
核心语言和内置标准包
添加外部包的方式
推荐使用的外部包
开发工具环境
下面几节会列出本书所需或推荐的 Python 工具。
这些工具可能会随着时间变化!Python 的包管理和开发工具一直是移动靶,时不时就会出现更好的解决方案。
入门
你应该能够编写并运行一个像示例 2-1 这样的 Python 程序。
示例 2-1 这个 Python 程序是这样运行的:this.py
def paid_promotion():
print("(that calls this function!)")
print("This is the program")
paid_promotion()
print("that goes like this.")
要在文本窗口或终端中从命令行执行这个程序,我会使用 $ 提示符作为约定,也就是你的系统已经在央求你输入点什么了。你在提示符后输入的内容会以粗体显示。如果你把示例 2-1 保存到一个名为 this.py 的文件中,可以像示例 2-2 这样运行它。
示例 2-2 测试 this.py
$ python this.py
This is the program
(that calls this function!)
that goes like this.
有些代码示例会使用交互式 Python 解释器。只要你输入 python,得到的就是它:
$ python
Python 3.9.1 (v3.9.1:1e5d33e9b9, Dec 7 2020, 12:10:52)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
前几行内容与你的操作系统和 Python 版本有关。这里的 >>> 就是你的提示符。交互式解释器有一个很方便的额外特性:如果你输入变量名,它会为你打印这个变量的值:
>>> wrong_answer = 43
>>> wrong_answer
43
这对表达式也同样有效:
>>> wrong_answer = 43
>>> wrong_answer - 3
40
如果你对 Python 还比较陌生,或者想快速复习一下,请阅读接下来的几节。
Python 本身
最低要求是,你需要 Python 3.7。这包括类型提示和 asyncio 等特性,而这些都是 FastAPI 的核心要求。我建议至少使用 Python 3.9,因为它会有更长的支持生命周期。Python 的标准来源是 Python Software Foundation。
包管理
你会希望下载外部 Python 包,并安全地把它们安装到你的计算机上。完成这件事的经典工具是 pip。
但你要如何下载这个“下载器”呢?如果你是从 Python Software Foundation 安装的 Python,那么你应该已经有 pip 了。如果没有,请按照 pip 网站上的说明来获取它。在本书中,每当我介绍一个新的 Python 包时,都会包含用于下载它的 pip 命令。
虽然只使用老牌的 pip 就能做很多事情,但你很可能还会想使用虚拟环境,并考虑像 Poetry 这样的替代工具。
虚拟环境
pip 会下载并安装包,但它应该把这些包放在哪里呢?虽然标准 Python 及其包含的库通常会安装在你操作系统中的标准位置,但你可能不能,也很可能不应该,修改那里的任何东西。pip 会使用一个不同于系统目录的默认目录,这样你就不会踩到系统标准 Python 文件。你可以修改这个设置;关于你的操作系统的具体细节,请查看 pip 网站。
但我们经常会使用多个 Python 版本,或者为某个项目创建特定的安装环境,这样你就能确切知道其中包含哪些包。为此,Python 支持虚拟环境。虚拟环境本质上只是目录,在非 Unix 世界里也叫文件夹,pip 会把下载的包写入这些目录。当你激活一个虚拟环境时,你的 shell,也就是主系统命令解释器,在加载 Python 模块时会优先在那里查找。
用于完成这件事的程序是 venv,从 Python 3.4 起,它就已经包含在标准 Python 中。
我们创建一个名为 venv1 的虚拟环境。你可以把 venv 模块作为独立程序来运行:
$ venv venv1
或者作为 Python 模块运行:
$ python -m venv venv1
要把它变成你当前的 Python 环境,请运行这个 shell 命令。在 Linux 或 Mac 上如下;Windows 和其他系统请查看 venv 文档:
$ source venv1/bin/activate
现在,只要你运行 pip install,它就会把包安装到 venv1 下面。当你运行 Python 程序时,也会在那里找到你的 Python 解释器和模块。
要停用你的虚拟环境,请按 Control-D,适用于 Linux 或 Mac;或者输入 deactivate,适用于 Windows。
你可以创建其他环境,比如 venv2,然后通过停用和激活,在它们之间切换。虽然我希望你在命名上比我更有想象力。
Poetry
pip 和 venv 这种组合非常常见,于是人们开始把它们结合起来,以节省步骤,并避免那种 source shell 魔法。其中一个包是 Pipenv,但一个名为 Poetry 的较新竞争者正在变得更受欢迎。
在用过 pip、Pipenv 和 Poetry 之后,我现在更喜欢 Poetry。可以用 pip install poetry 来获取它。Poetry 有许多子命令,例如 poetry add 用来把一个包添加到你的虚拟环境,poetry install 用来实际下载并安装它,等等。请查看 Poetry 网站,或者运行 poetry 命令来获取帮助。
除了下载单个包之外,pip 和 Poetry 还会在配置文件中管理多个包:pip 使用 requirements.txt,Poetry 使用 pyproject.toml。Poetry 和 pip 不只是下载包,还会管理包之间可能存在的棘手依赖关系。你可以指定所需包版本的最小值、最大值、范围,或者精确值,也就是所谓的固定版本。随着你的项目增长,以及它所依赖的包发生变化,这一点可能变得非常重要。如果你使用的某个功能是在某个包的特定版本中首次出现的,你可能需要指定最低版本;如果某个功能后来被移除了,你可能需要指定最高版本。
源代码格式化
源代码格式化没有前面几节的话题那么重要,但仍然有帮助。为了避免关于代码格式的争论,也就是 bikeshedding,可以使用一个工具把源码整理成标准、不奇怪的格式。一个不错的选择是 Black。可以用下面的命令安装它:
pip install black
测试
测试会在第 12 章中详细介绍。虽然 Python 标准测试包是 unittest,但大多数 Python 开发者使用的工业强度 Python 测试包是 pytest。可以用下面的命令安装它:
pip install pytest
源代码控制和持续集成
如今,源代码控制几乎通用的解决方案是 Git,代码存储库,也就是 repo,则位于 GitHub 和 GitLab 等网站上。使用 Git 并不是 Python 或 FastAPI 特有的事情,但你很可能会把大量开发时间花在 Git 上。pre-commit 工具会在提交到 Git 之前,在你的本地机器上运行各种测试,比如 black 和 pytest。推送到远程 Git 仓库之后,那里还可能运行更多持续集成,也就是 CI 测试。
第 12 章和“故障排除”部分会提供更多细节。
Web 工具
第 3 章会展示如何安装和使用本书中用到的主要 Python Web 工具:
FastAPI
Web 框架本身
Uvicorn
一个异步 Web 服务器
HTTPie
一个文本 Web 客户端,类似于 curl
Requests
一个同步 Web 客户端包
HTTPX
一个同步/异步 Web 客户端包
API 和服务
Python 的模块和包对于创建大型应用程序至关重要,它们可以避免应用程序变成“大泥球”。即使在单进程 Web 服务中,你也可以通过精心设计模块和导入关系,维持第 1 章讨论过的分离。
Python 的内置数据结构极其灵活,也非常诱人,容易让人想在各处都使用它们。但在后续章节中,你会看到我们可以定义更高层级的模型,让层与层之间的通信更加干净。这些模型依赖 Python 中一个相对较新的特性,叫作类型提示。我们来进入这个话题,不过先简要插入说明一下 Python 如何处理变量。这不会疼。
变量是名字
“对象”这个术语在软件世界中有很多定义,也许太多了。在 Python 中,对象是一种数据结构,它包装了程序中的每一个独立数据片段,从像 5 这样的整数,到函数,再到任何你可能自己定义的东西。除其他记账信息外,它还指定以下内容:
一个唯一身份值
与硬件匹配的低层级类型
具体值,也就是物理比特
引用它的变量数量,也就是引用计数
Python 在对象层级是强类型的,也就是说对象的类型不会改变,尽管它的值可能会改变。如果一个对象的值可以被改变,它就被称为可变对象;如果不能改变,就是不可变对象。
但在变量层级,Python 与许多其他计算语言不同,而这可能让人困惑。在许多其他语言中,变量本质上是指向某块内存区域的直接指针,这块内存中包含一个原始值,以符合计算机硬件设计的比特形式存储。如果你给这个变量赋一个新值,这门语言就会用新值覆盖内存中的旧值。
这很直接,也很快。编译器会跟踪什么东西放在哪里。这也是 C 这类语言比 Python 更快的原因之一。作为开发者,你需要确保只给每个变量赋予正确类型的值。
现在,这里就是 Python 的重大差异:Python 变量只是一个名字,临时关联到内存中的一个更高层级对象。如果你给一个指向不可变对象的变量赋一个新值,实际上是在创建一个包含该值的新对象,然后让这个名字指向这个新对象。旧对象,也就是这个名字过去指向的对象,随后就变成空闲状态;如果没有其他名字仍然引用它,也就是它的引用计数为 0,那么它的内存就可以被回收。
在《Introducing Python》(O’Reilly)中,我把对象比作放在内存架子上的塑料盒子,而名字或变量则像贴在这些盒子上的便利贴。或者,你也可以把名字想象成用绳子挂在这些盒子上的标签。
通常,当你使用一个名字时,会把它分配给一个对象,并让它保持关联。这种简单的一致性有助于你理解自己的代码。变量的作用域是代码中某个名字指向同一个对象的区域,比如在一个函数内部。你可以在不同作用域中使用同一个名字,但每个名字都指向不同对象。
虽然你可以让一个变量在整个 Python 程序中指向不同对象,但这并不一定是好实践。不仔细看代码,你并不知道第 100 行的名字 x 是否与第 20 行的名字 x 处于同一个作用域。顺便说一句,x 是一个很糟糕的名字。我们应该选择真正能传达某些含义的名字。
类型提示
所有这些背景说明都有一个目的。
Python 3.6 添加了类型提示,用来声明一个变量所引用对象的类型。Python 解释器在运行时并不会强制执行这些提示!相反,它们可以被各种工具使用,以确保你对某个变量的使用是一致的。标准类型检查器叫作 mypy,后面我会展示它如何使用。
类型提示可能看起来只是一个不错的小东西,就像程序员用来避免错误的许多 lint 工具一样。例如,它可能会提醒你,变量 count 指向的是一个类型为 int 的 Python 对象。但这些提示,尽管只是可选且不被强制执行的注释,字面意义上说就是提示,却被证明有一些意想不到的用途。在本书后面,你会看到 FastAPI 如何适配 Pydantic 包,巧妙地利用类型提示。
添加类型声明也许是其他曾经无类型语言中的一个趋势。例如,许多 JavaScript 开发者已经转向 TypeScript。
数据结构
你将在第 5 章中看到关于 Python 和数据结构的详细内容。
Web 框架
除其他事情之外,Web 框架会在 HTTP 字节和 Python 数据结构之间进行转换。它可以为你节省大量工作。另一方面,如果其中某个部分无法按照你的需要工作,你可能就需要 hack 出一个解决方案。俗话说,不要重新发明轮子,除非你找不到一个圆的。
Web Server Gateway Interface,也就是 WSGI,是一个同步的 Python 标准规范,用来把应用程序代码连接到 Web 服务器。传统 Python Web 框架都是构建在 WSGI 之上的。但同步通信可能意味着对某个远慢于 CPU 的东西进行忙等待,比如磁盘或网络。然后,你就会寻找更好的并发能力。近年来,并发变得越来越重要。因此,Python Asynchronous Server Gateway Interface,也就是 ASGI 规范,被开发了出来。第 4 章会讨论这个内容。
Django
Django 是一个功能完整的 Web 框架,它给自己的标签是“为有截止日期的完美主义者打造的 Web 框架”。它由 Adrian Holovaty 和 Simon Willison 于 2003 年发布,并以 20 世纪比利时爵士吉他手 Django Reinhardt 的名字命名。Django 经常用于由数据库支持的企业网站。第 7 章中我会包含更多关于 Django 的细节。
Flask
相比之下,Flask 是由 Armin Ronacher 于 2010 年推出的微框架。第 7 章会介绍更多关于 Flask 的信息,以及它如何与 Django 和 FastAPI 比较。
FastAPI
在舞会上见过其他追求者之后,我们终于遇到了引人入胜的 FastAPI,也就是本书的主题。虽然 FastAPI 是 Sebastián Ramírez 于 2018 年发布的,但它已经攀升到 Python Web 框架的第三位,仅次于 Flask 和 Django,并且增长速度更快。一项 2022 年的比较显示,它可能会在某个时候超过它们。
注意
截至 2023 年 10 月底,GitHub star 数量如下:
Django:73.8 千
Flask:64.8 千
FastAPI:64 千
在仔细研究了各种替代方案之后,Ramírez 提出了一个设计,这个设计很大程度上基于两个第三方 Python 包:
Starlette
负责 Web 细节
Pydantic
负责数据细节
然后,他又加入了自己的配料和秘制酱汁,形成了最终产品。你会在下一章看到我的意思。
回顾
本章涵盖了许多与当今 Python 相关的内容:
Python Web 开发者有用的工具
API 和服务的重要性
Python 的类型提示、对象和变量
用于 Web 服务的数据结构
Web 框架