理解字符串格式化中的 Python %g,实现 Java String.format 行为

Posted

技术标签:

【中文标题】理解字符串格式化中的 Python %g,实现 Java String.format 行为【英文标题】:Understanding the Python %g in string formatting, achieving Java String.format behavior 【发布时间】:2017-06-09 23:38:54 【问题描述】:

在 Java 中:

String test1 = String.format("%7.2g", 3e9);
System.out.println(test1);

这打印3.0e+09

在 Python 2.7 中,如果我运行此代码

for num in [3e9, 3.1e9, 3.01e9, 3e2, 3.1e2, 3.01e2]:
   print '%7.2g %7.2f %7.2e' % (num, num, num)

我明白了

  3e+09 3000000000.00 3.00e+09
3.1e+09 3100000000.00 3.10e+09
  3e+09 3010000000.00 3.01e+09
  3e+02  300.00 3.00e+02
3.1e+02  310.00 3.10e+02
  3e+02  301.00 3.01e+02

嗯?看起来精度 (.2) 参数在 Python 的 %g 中的处理方式与 Python 的 %f、Python 的 %e 或 Java 的 %g 完全不同。这是doc(我的重点):

一般格式。对于给定的精度 p >= 1,这会将数字四舍五入为 p 个有效数字,然后根据其大小将结果格式化为定点格式或科学计数法。

精确的规则如下:假设用表示类型'e'和精度p-1格式化的结果将具有指数exp。然后如果 -4 在这两种情况下,都会从有效数字中删除不重要的尾随零,如果小数点后面没有剩余数字,也会删除小数点。

正负无穷、正负零和nans,无论精度如何,都分别格式化为inf、-inf、0、-0和nan。

0 的精度等同于 1 的精度。默认精度为 6。

WTF?有什么办法可以防止那些尾随零被删除?字符串格式化的重点是实现某种一致性,例如用于文本对齐。

有没有什么方法可以让 Java 行为(本质上是小数点右侧的有效位数)而不必从头开始重写整个事情?

【问题讨论】:

【参考方案1】:

使用format 方法,您可以执行以下操作:

for num in [3e9, 3.1e9, 3.01e9, 3e2, 3.1e2, 3.01e2]:
    print ('n:7.2c n:7.2f n:7.2e'.format(n=num, c='e' if num > 1e4 else 'f'))

输出:

3.00e+09 3000000000.00 3.00e+09
3.10e+09 3100000000.00 3.10e+09
3.01e+09 3010000000.00 3.01e+09
 300.00  300.00 3.00e+02
 310.00  310.00 3.10e+02
 301.00  301.00 3.01e+02

其中有两部分可能不太为人所知。

1。参数化字符串格式

除了简单的格式化:

>>> ''.format(3.5)
'3.5'

并使用更详细的结果规范进行格式化:

>>> ':5.2f'.format(3.5)
' 3.50'

您可以在format中使用关键字参数,您可以在字符串中访问:

>>> 'num:5.2f'.format(num=3.5)
' 3.50'

您也可以将这些用于格式规范本身:

>>> ':5.decif'.format(3.5, deci=3)
'3.500'

2。 if 表达式

除了if 语句之外,还有一个if 表达式,也就是ternary operator。

所以,这个表达式:

a = 1
b = 2
res = 10 if a < b else 20

相当于这个语句:

if a < b:
    res = 10
else:
    res= 20

将两者放在一起会产生如下结果:

'num:7.2c'.format(num=num, c='e' if num > 1e4 else 'f')

【讨论】:

嗯...有趣!我想一旦我理解了你的答案,我就会接受它。 添加了一些解释以方便理解。 ;) hmmmm... 哦,所以您只是根据num 的大小选择%7.2e%7.2f(顺便说一下,您需要abs)。那样不行,这两种语言的字符串格式化逻辑之间存在根本区别。【参考方案2】:

python 所做的格式与 C 的 printf 样式格式更一致,它也为 g 转换删除尾随零。既然python的参考实现是C语言的,为什么要和Java保持一致呢?

当使用% 运算符进行字符串格式化时,相关文档为String Formatting Operations,它与您链接的文档有一些不同,特别是它允许# 替代形式g

替代形式导致结果始终包含小数点,并且不会像其他情况那样删除尾随零。

精度决定小数点前后的有效位数,默认为6。

所以在你的情况下:

>>> "%#7.2g" % 3e9
3.0e+09

这与str.format() 所允许的不同,# 用于启用二进制、八进制或十六进制输出的前缀(至少在 python2 中,这在 python3 中已更改)。

【讨论】:

以上是关于理解字符串格式化中的 Python %g,实现 Java String.format 行为的主要内容,如果未能解决你的问题,请参考以下文章

Python_字符串格式化

yield的作用理解

计算字符串中的字母频率(Python)[重复]

Python字符串格式化

Python 3初学者,如何实现for循环打印一个字符串,比如“abcdef”,按照如下格式

Python 字符串格式中的位置参数:str.format vs f-string