尝试使用格式说明符打印一个小于 1 且没有前导零的浮点数。我想出了一些技巧,但我认为有一种方法可以删除格式说明符中的前导零。我在文档中找不到它。
问题
>>> k = .1337
>>> print "%.4f" % k
'0.1337'
哈克
>>> print ("%.4f" % k) [1:]
'.1337'
尝试使用格式说明符打印一个小于 1 且没有前导零的浮点数。我想出了一些技巧,但我认为有一种方法可以删除格式说明符中的前导零。我在文档中找不到它。
问题
>>> k = .1337
>>> print "%.4f" % k
'0.1337'
哈克
>>> print ("%.4f" % k) [1:]
'.1337'
这是另一种方式:
>>> ("%.4f" % k).lstrip('0')
'.1337'
它比[1:]
它也适用于> = 1的数字更通用。
然而,这两种方法都不能正确处理负数。在这方面,以下方法更好:
>>> re.sub('0(?=[.])', '', ("%0.4f" % -k))
'-.1337'
不是特别优雅,但现在我想不出更好的方法。
尽管我喜欢可爱的正则表达式技巧,但我认为一个简单的函数是最好的方法:
def formatFloat(fmt, val):
ret = fmt % val
if ret.startswith("0."):
return ret[1:]
if ret.startswith("-0."):
return "-" + ret[2:]
return ret
>>> formatFloat("%.4f", .2)
'.2000'
>>> formatFloat("%.4f", -.2)
'-.2000'
>>> formatFloat("%.4f", -100.2)
'-100.2000'
>>> formatFloat("%.4f", 100.2)
'100.2000'
这样做的好处是易于理解,部分原因startswith
是它是简单的字符串匹配而不是正则表达式。
一种可行的选择,无需正则表达式且负数大于 10
k = -.1337
("%.4f" % k).replace("-0","-").lstrip("0")
您可以使用以下MyFloat
类而不是内置float
类。
def _remove_leading_zero(value, string):
if 1 > value > -1:
string = string.replace('0', '', 1)
return string
class MyFloat(float):
def __str__(self):
string = super().__str__()
return _remove_leading_zero(self, string)
def __format__(self, format_string):
string = super().__format__(format_string)
return _remove_leading_zero(self, string)
使用此类,您必须使用str.format
函数而不是模运算符 ( %
) 进行格式化。以下是一些示例:
>>> print(MyFloat(.4444))
.4444
>>> print(MyFloat(-.4444))
-.4444
>>> print('some text {:.3f} some more text',format(MyFloat(.4444)))
some text .444 some more text
>>> print('some text {:+.3f} some more text',format(MyFloat(.4444)))
some text +.444 some more text
如果您还想让类的模运算符 ( %
)str
以相同的方式运行,那么您必须通过子类化类来覆盖类的__mod__
方法。str
但它不会像覆盖类的__format__
方法那么容易float
,因为在这种情况下,格式化的浮点数可能出现在结果字符串的任何位置。
[注:以上所有代码都是用 Python3 编写的。您还必须__unicode__
在 Python2中覆盖并且还必须更改super
调用。]
PS:你也可以重写__repr__
类似的方法__str__
,如果你也想改变官方字符串MyFloat
表示。
编辑:实际上您可以使用方法添加新语法来格式化字符串__format__
。因此,如果您想保留这两种行为,即在需要时显示前导零,并且在不需要时不显示前导零。MyFloat
您可以按如下方式创建类:
class MyFloat(float):
def __format__(self, format_string):
if format_string.endswith('z'): # 'fz' is format sting for floats without leading the zero
format_string = format_string[:-1]
remove_leading_zero = True
else:
remove_leading_zero = False
string = super(MyFloat, self).__format__(format_string)
return _remove_leading_zero(self, string) if remove_leading_zero else string
# `_remove_leading_zero` function is same as in the first example
并按如下方式使用此类:
>>> print('some text {:.3f} some more text',format(MyFloat(.4444)))
some text 0.444 some more text
>>> print('some text {:.3fz} some more text',format(MyFloat(.4444)))
some text .444 some more text
>>> print('some text {:+.3f} some more text',format(MyFloat(.4444)))
some text +0.444 some more text
>>> print('some text {:+.3fz} some more text',format(MyFloat(.4444)))
some text +.444 some more text
>>> print('some text {:.3f} some more text',format(MyFloat(-.4444)))
some text -0.444 some more text
>>> print('some text {:.3fz} some more text',format(MyFloat(-.4444)))
some text -.444 some more text
请注意,使用 'fz' 而不是 'f' 会删除前导零。
此外,上面的代码在 Python2 和 Python3 中都有效。
import re
re.sub("^(\-?)0\.", r'\1.', "%.4f" % k)
这很短,很简单,我找不到它不起作用的场景。
例子:
>>> import re
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % 0)
'.0000'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % 0.1337)
'.1337'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % 1.337)
'1.3370'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % -0)
'.0000'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % -0.1337)
'-.1337'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % -1.337)
'-1.3370'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % 10.337)
'10.3370'
>>> re.sub("^(\-?)0\.", r'\1.', "%.4f" % -10.337)
'-10.3370'
编辑:如果您只考虑数字 > -10 和 < 10 以下将起作用:
("%.4f", k).replace('0.', '.')
我宁愿追求可读性和简单性:让我们独立处理符号和数字。还有一点内联的 if 语句永远不会伤害任何人。
k = -.1337
"".join( ["-" if k < 0 else "", ("%.4f" % abs(k)).lstrip('0')] )
使用.lstrip()
, 后使用字符串格式转换为字符串:
>>> k = .1827412
>>> print ("%.4f"%(k)).lstrip('0')
.1827
>>>
.lstrip()
可用于删除字符串的任何前导字符:
>>> k = 'bhello'
>>> print k.lstrip('b')
hello
>>> print k.lstrip('bhel')
o
>>> print k.lstrip('bel')
hello
>>>
从文档:
string.lstrip(s[, 字符])
返回删除前导字符的字符串副本
我很惊讶没有人提出更数学的方法来做到这一点:
n = 0.123456789
'.%d' % (n*1e4)
对我来说看起来好多了。:)
但有趣的是,你的速度最快。
$ python -mtimeit '".%d" % (0.123456789*1e4)'
1000000 loops, best of 3: 0.809 usec per loop
$ python -mtimeit '("%.4f"%(0.123456789)).lstrip("0")'
1000000 loops, best of 3: 0.209 usec per loop
$ python -mtimeit '("%.4f"%(0.123456789))[1:]'
10000000 loops, best of 3: 0.0723 usec per loop
由于我们只考虑 > -1 到 < 1,因此以下编辑将起作用。
import re
re.sub(r"0+\.", ".", %0.4f" % k)
这将保持符号,仅删除小数点左侧的数字。
Python 的标准库的 str.format() 可用于生成浮点值的字符串转换。然后可以对字符串进行操作以形成正数或负数的最终结果。
n = -.1234567890
print('{0}'.format('-' if n < 0 else '') + ('{:0.4}'.format(n).split('-')[1 if n < 0 else 0].lstrip('0')))
当您想要简单的东西并且不需要负数支持时:
f'{k:.4f}'.lstrip('0')
当您需要负数支持时,还有其他解决方案,包括 @nettux443 的出色正则表达式。
一个超短的(虽然不是很pythonic)单行,它也处理负数:
k = -.1337
"-"*(k<0)+("%.4f"%abs(k)).lstrip('0')
问题是打印一个没有前导零的浮点数,无论浮点数的符号如何。前导零总是在小数点之前。将打印的浮点数拆分为“0.”,然后将结果列表重新加入到“.”周围,如下所示:
>> flt = -.31415926
-0.31415926
>> '%.4f' % flt # prints leading zero
'-0.3142'
>> '.'.join( ('%.4f' % flt).split('0.')) # removes leading zero
'-.3142'