PEP 331 – 独立于区域设置的浮点数/字符串转换
- 作者:
- Christian R. Reis <kiko at async.com.br>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2003年7月19日
- Python 版本:
- 2.4
- 发布历史:
- 2003年7月21日,2003年8月13日,2004年6月18日
摘要
Python 2.3 中对 LC_NUMERIC 区域设置类别的支持仅在 Python 空间中实现。这导致使用 C 语言实现并解析和生成浮点数的扩展模块和库的应用程序出现不一致行为和线程安全问题。本文档提出了一个计划,通过提供和使用必要的替代区域设置无关函数来消除这种不一致性。
引言
Python 通过 locale 模块提供通用的本地化服务,该模块除了其他功能外,还允许本地化数值类型的显示和转换过程。区域设置类别,例如 LC_TIME 和 LC_COLLATE,允许精确配置应用程序的哪些方面需要本地化。
LC_NUMERIC 类别指定非货币数字信息的格式,例如浮点数和定点数中的小数分隔符。LC_NUMERIC 类别的本地化目前仅在 Python 空间中实现;从 Python 运行时调用的 C 库不知道 Python 的 LC_NUMERIC 设置。这样做是为了避免更改 Python 解析器和相关代码使用的某些低级函数的行为 [2]。
然而,这给封装 C 库的扩展模块带来了问题。使用这些扩展模块的应用程序将不一致地显示和转换浮点值。
PyGTK [3] 的作者 James Henstridge 额外指出,setlocale() 函数也存在线程安全问题,因为一个线程可以在 GIL 之外调用 C 库 setlocale(),并导致 Python 错误地解析和生成浮点数。
基本原理
Python 和 C 库的 LC_NUMERIC 本地化之间不一致是任何使用 C 扩展的本地化应用程序的问题。问题的确切性质将因应用程序而异,但它很可能在解析或格式化浮点值时发生。
问题示例
促成此 PEP 的最初问题与 GTK+ UI 工具包中的 GtkSpinButton [4] 小部件有关,该小部件由 PyGTK 模块封装。该小部件可以设置为数字模式,当发生这种情况时,输入到其中的字符将被评估为数字。
当 LC_NUMERIC 设置为浮点分隔符与 C 区域设置标准不同的区域设置时(例如,巴西区域设置 pt_BR 的 ',' 而不是 '.'),就会出现问题。由于 LC_NUMERIC 未在 libc 级别设置,因此浮点值在 spinbutton 的文本输入中显示不正确(使用 '.' 作为分隔符),并且无法使用 ',' 分隔符输入小数。
这个小例子表明,当使用 Python 编码时,使用此工具包的本地化应用程序的可用性会降低。
提案
Martin v. Löwis 在 python-dev 上评论了问题可接受解决方案的最初限制
LC_NUMERIC可以在 C 库级别设置而不会破坏解析器。float()和str()保持区域设置无关。- 区域设置感知的
str()和atof()留在 locale 模块中。
对 Python 源的分析表明,以下函数目前依赖于 LC_NUMERIC 设置为 C 区域设置
Python/compile.c:parsenumber()Python/marshal.c:r_object()Objects/complexobject.c:complex_to_buf()Objects/complexobject.c:complex_subtype_from_string()Objects/floatobject.c:PyFloat_FromString()Objects/floatobject.c:format_float()Objects/stringobject.c:formatfloat()Modules/stropmodule.c:strop_atof()Modules/cPickle.c:load_float()
建议的方法是实现 LC_NUMERIC 无关的函数,用于从 (strtod()/atof()) 到 (snprintf()) 浮点格式的转换,并在格式不应根据用户指定的区域设置而变化的地方使用这些函数。
locale 模块也应进行更改,以删除对 LC_NUMERIC 的特殊处理。
此更改也应解决上述线程安全问题。
潜在的代码贡献
此问题最初被报告为 GTK+ 库 [5] 中的问题;此后,它被正确诊断为 Python 实现中的不一致。然而,巧合的是,glib 库(主要为 GTK+ 开发,不要与 GNU C 库混淆)出于与本文中提出的原因类似的原因,实现了许多 LC_NUMERIC 无关的函数(例如,请参阅 [6])。
在同一 GTK+ 问题报告中,Havoc Pennington 建议 glib 作者愿意将此代码贡献给 PSF,这将大大简化此 PEP 的实现。glib 代码的原始作者 Alex Larsson 于 2003 年 8 月 20 日提交了 PSF 贡献者协议 [7] [8],以确保代码可以安全集成;该协议已收到并接受。
风险
提供的区域设置无关函数可能存在跨平台问题,尽管鉴于提供的代码只是撤销了对浮点数所做的任何区域设置相关更改,此风险较低。
Martin 和 Guido 指出了贡献代码中潜在的版权问题。我相信我们在这方面不会有问题,因为 GTK+ 和 glib 团队的成员表示他们同意重新许可代码,并且 PSF 贡献者协议已经邮寄以确保此安全性。
Tim Peters 指出 [9],在涉及线程的情况下,建议的更改不足以完全解决问题。然而,目前还没有一个完整的解决方案。
实施
Gustavo Carneiro <gjc at inescporto.pt> 开发了一个实现,并将其附加到 Sourceforge.net 错误 774665 [10]
最终补丁 [11] 已由 Martin v. Löwis 于 2004 年 6 月 8 日集成到 Python CVS 中,如错误报告所述。
参考资料
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0331.rst