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 的作者 James Henstridge[3] 还指出,setlocale()
函数也存在线程安全问题,因为线程可能会在 GIL 外调用 C 库setlocale()
,并导致 Python 错误地解析和生成浮点数。
基本原理
Python 和 C 库本地化之间在LC_NUMERIC
方面的差异对于任何使用 C 扩展的本地化应用程序来说都是一个问题。问题的具体性质将根据应用程序而有所不同,但最有可能发生在解析或格式化浮点值时。
示例问题
促使这个 PEP 的最初问题与 GTK+ UI 工具包中 GtkSpinButton[4] 小部件(由 PyGTK 模块包装)有关。该小部件可以设置为数字模式,在这种模式下,键入其中的字符将被评估为数字。
当LC_NUMERIC
设置为浮点分隔符与 C 区域设置的标准不同的区域设置(例如,对于巴西区域设置 pt_BR,使用“,”而不是“.”)时,就会出现问题。因为LC_NUMERIC
没有在 libc 级别设置,所以浮点值在自旋按钮的文本输入中显示不正确(使用“.”作为分隔符),并且无法使用“,”分隔符输入小数。
这个小例子演示了当使用 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 的实现。Alex Larsson(glib 代码的最初作者)于 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