Python 是为谁设计的?
几年前,我在 python-dev 邮件列表中,以及在活跃的 CPython 核心开发人员和认为参与这一过程不是有效利用个人时间和精力的人中强调说,“CPython 的发展太快了也太慢了” 是很多冲突的原因之一。
我一直认为事实确实如此,但这也是一个要点,在这几年中我也花费了很多时间去反思它。在我写那篇文章的时候,我还在波音防务澳大利亚公司(Boeing Defence Australia)工作。下个月,我离开了波音进入红帽亚太(Red Hat Asia-Pacific),并且开始在大企业的开源供应链管理方面取得了 再分发者 redistributor 层面的视角。
我尝试将 CPython 的使用情况分解如下,尽管看起来有些过于简化(注意,这些分类的界线并不是很清晰,他们仅关注于考虑新软件特性和版本发布后不同因素的影响):
从根本上说,CPython 和标准库的主要受众是哪些呢?是那些不管出于什么原因,将有限的标准库和从 PyPI 显式声明安装的第三方库组合起来所提供的服务还不能够满足需求的那些人。
为了更进一步简化上面回顾的不同用法和部署模型,宏观地将最大的 Python 用户群体分开来看,一类是在一些感兴趣的环境中将 Python 作为一种 脚本语言 使用的人;另外一种是将它用作一个 应用程序开发语言 的人,他们最终发布的是一种产品而不是他们的脚本。
把 Python 作为一种脚本语言来使用的开发者的典型特性包括:
相比之下,使用 Python 作为一个应用程序开发语言的开发者特征包括:
pyproject.toml
、requirements.txt
、Pipfile
),要么是作为生成的发行包的一部分(如 setup.py
、flit.ini
)作为以上分类的一个结果,CPython 和标准库的主要用途是,在相应的 CPython 特性发布后,为教育和 临时 ad hoc 的 Python 脚本环境提供 3-5 年基础维护服务。
对于临时脚本使用的情况,这个 3-5 年的延迟是由于再分发者给用户开发新版本的延迟造成的,以及那些再分发版本的用户们花在修改他们的标准操作环境上的时间。
对于教育环境中的情况是,教育工作者需要一些时间去评估新特性,然后决定是否将它们包含进教学的课程中。
这篇文章很大程度上是受 Twitter 上对我的这个评论的讨论的启发,它援引了定义在 PEP 411 中 临时 Provisional API 的情形,作为一个开源项目的例子,对用户发出事实上的邀请,请其作为共同开发者去积极参与设计和开发过程,而不是仅被动使用已准备好的最终设计。
这些回复包括一些在更高级别的库中支持临时 API 的困难程度的一些沮丧性表述,没有这些库做临时状态的传递,因此而被限制为只有临时 API 的最新版本才支持这些相关特性,而不是任何早期版本的迭代。
我的主要回应是,建议开源提供者应该强制实施有限支持,通过这种强制的有限支持可以让个人的维护努力变得可持续。这意味着,如果对临时 API 的老版本提供迭代支持是非常痛苦的,那么,只有在项目开发人员自己需要、或有人为此支付费用时,他们才会去提供支持。这与我的这个观点是类似的,那就是,志愿者提供的项目是否应该免费支持老的、商业性质的、长周期的 Python 版本,这对他们来说是非常麻烦的事,我不认为他们应该这样做,正如我所期望的那样,大多数这样的需求都来自于管理差劲的惯性,而不是真正的需求(真正的需求,应该去支付费用来解决问题)。
而我的第二个回应是去实现这一点,尽管多年来一直在讨论这个问题(比如,在上面链接中最早在 2011 年的一篇的文章中,以及在 Python 3 问答的回复中的这里、这里、和这里,以及去年的这篇文章 Python 包生态系统中也提到了一些),但我从来没有真实地尝试直接去解释它在标准库设计过程中的影响。
如果没有这些背景,设计过程中的一部分,比如临时 API 的引入,或者是 受启发而不同于它 inspired-by-not-the-same-as 的引入,看起来似乎是完全没有意义的,因为他们看起来似乎是在尝试对 API 进行标准化,而实际上并没有。
任何提交给 python-ideas 或 python-dev 的提案所面临的第一个门槛就是清楚地回答这个问题:“为什么 PyPI 上的模块不够好?”。绝大多数的提案都在这一步失败了,为了通过这一步,这里有几个常见的话题:
secrets
库的原因:它使得人们很少去使用 random
模块,由于安全敏感的原因,它预期用于游戏和模拟统计)asyncio
、wsgiref
、unittest
、和 logging
都是这种情况)enum
就是这种情况,像 unittest
一样)contextlib
、asyncio
和 typing
)pathlib
和 ipaddress
)statistics
模块允许进行交互式地探索统计的概念,尽管你可能根本就不会用它来做完整的统计分析)只通过了前面的 “PyPI 是不是明显不够好” 的检查,一个模块还不足以确保被纳入标准库中,但它已经足以将问题转变为 “在未来几年中,你所推荐的要包含的库能否对一般的入门级 Python 开发人员的经验有所提升?”
标准库中的 ensurepip
和 venv
模块的引入也明确地告诉再分发者,我们期望的 Python 级别的打包和安装工具在任何平台的特定分发机制中都予以支持。
现有的第三方模块有时候会被批量地采用到标准库中,在其它情况下,实际上添加的是吸收了用户对现有 API 体验之后进行重新设计和重新实现的 API,但是会根据另外的设计考虑和已经成为其中一部分的语言实现参考来进行一些删除或细节修改。
例如,与流行的第三方库 path.py
、pathlib
的前身不同,它们并没有定义字符串子类,而是以独立的类型替代。作为解决文件互操作性问题的结果,定义了文件系统路径协议,它允许使用文件系统路径的接口去使用更多的对象。
为了在 “IP 地址” 这个概念的教学上提供一个更好的工具,ipaddress
模块设计调整为明确地将主机接口定义与地址和网络的定义区分开(IP 地址被关联到特定的 IP 网络),而最原始的 ipaddr
模块中,在网络术语的使用方式上不那么严格。
另外的情况是,标准库将综合多种现有的方法的来构建,以及为早已存在的库定义 API 时,还有可能依赖不存在的语法特性。比如,asyncio
和 typing
模块就全部考虑了这些因素,虽然在 PEP 557 中正在考虑将后者所考虑的因素应用到 dataclasses
API 上。(它可以被总结为 “像属性一样,但是使用可变注释作为字段声明”)。
这类修改的原理是,这类库不会消失,并且它们的维护者对标准库维护相关的那些限制通常并不感兴趣(特别是相对缓慢的发布节奏)。在这种情况下,在标准库文档的更新版本中使用 “See Also” 链接指向原始模块的做法非常常见,尤其是在第三方版本额外提供了标准库模块中忽略的那些特性时。
虽然 CPython 维护了 API 的弃用策略,但在没有正当理由的情况下,我们通常不会去使用该策略(在其他项目试图与 Python 2.7 保持兼容性时,尤其如此)。
然而在实践中,当添加这种受已有的第三方启发而不是直接精确拷贝第三方设计的新 API 时,所承担的风险要高于一些正常设计决定可能出现问题的风险。
当我们考虑到这种改变的风险比平常要高,我们将相关的 API 标记为临时,表示保守的终端用户要避免完全依赖它们,而共享抽象层的开发者可能希望对他们准备去支持的那个临时 API 的版本考虑实施比平时更严格的限制。
这里简短的回答得到升级的主要 API 有哪些:
如果在将模块用于应用程序开发目的时(如 datetime
),现有模块的限制主要是显而易见的,如果再分发者通过第三方方案很容易地实现了改进,(如 requests
),或者如果标准库的发布节奏与所需要的包之间真的存在冲突,(如 certifi
),那么,建议对标准库版本进行改变的因素将显著减少。
从本质上说,这和上面关于 PyPI 问题正好相反:因为从应用程序开发人员体验的角度来说,PyPI 的分发机制通常已经够好了,这种分发方式的改进是有意义的,允许再分发者和平台提供者自行决定将哪些内容作为他们缺省提供的一部分。
假设在 3-5 年时间内,缺省出现了被认为是改变带来的可感知的价值时,才会将这些改变纳入到 CPython 和标准库中。
是的,就像是 ensurepip
使用的捆绑模式(CPython 发行了一个 pip
的最新捆绑版本,而并没有把它放进标准库中),将来可能被应用到其它模块中。
最有可能的第一个候选者是 distutils
构建系统,因为切换到这种模式将允许构建系统在多个发行版本之间保持一致。
这种处理方式的其它可能候选者是 Tcl/Tk 图形套件和 IDLE 编辑器,它们已经被拆分,并且一些开发者将其改为可选安装项。
从本质上说,那些积极参与开源开发的人就是那些致力于开源应用程序和共享抽象层的人。
那些写一些临时脚本或为学生设计一些教学习题的人,通常不认为他们是软件开发人员 —— 他们是教师、系统管理员、数据分析人员、金融工程师、流行病学家、物理学家、生物学家、市场研究员、动画师、平面设计师等等。
对于一种语言,当我们全部的担心都是开发人员的经验时,那么我们就可以根据人们所知道的内容、他们使用的工具种类、他们所遵循的开发流程种类、构建和部署他们软件的方法等假定,来做大量的简化。
当应用程序运行时作为脚本引擎广泛流行时,事情会变得更加复杂。做好任何一项工作已经很困难,并且作为单个项目的一部分来平衡两个受众的需求会导致双方经常不理解和不相信。
这篇文章不是为了说明我们在开发 CPython 过程中从来没有做出过不正确的决定 —— 它只是去合理地回应那些对添加到 Python 标准库中的看上去很荒谬的特性的质疑,它将是 “我不是那个特性的预期目标受众的一部分”,而不是 “我对它没有兴趣,因此它对所有人都是毫无用处和没有价值的,添加它纯属骚扰我”。
via: http://www.curiousefficiency.org/posts/2017/10/considering-pythons-target-audience.html
作者:Nick Coghlan 译者:qhwdw 校对:wxy、pityonline