形式指定子を使用して、先頭のゼロなしで 1 未満になる float を出力しようとしています。ちょっとしたハックを思いつきましたが、フォーマット指定子の先頭のゼロを削除する方法があると思います。ドキュメントで見つけられませんでした。
問題
>>> k = .1337
>>> print "%.4f" % k
'0.1337'
ハック
>>> print ("%.4f" % k) [1:]
'.1337'
形式指定子を使用して、先頭のゼロなしで 1 未満になる float を出力しようとしています。ちょっとしたハックを思いつきましたが、フォーマット指定子の先頭のゼロを削除する方法があると思います。ドキュメントで見つけられませんでした。
問題
>>> 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
これには、正規表現ではなく単純な文字列一致であるため、理解しやすいという利点があります。
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 で書かれています。また、Python2でオーバーライド__unicode__
する必要があり、呼び出しも変更する必要があり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
「f」の代わりに「fz」を使用すると、先頭のゼロが削除されることに注意してください。
また、上記のコードは Python2 と Python3 の両方で機能します。
誰もそれを行うためのより数学的な方法を提案していないことに驚いています:
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
.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[, chars])
先頭の文字を削除した文字列のコピーを返します
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')] )
Python の標準 lib の str.format() を使用して、float 値の文字列変換を生成できます。次に、文字列を操作して、正または負の数値の最終結果を形成できます。
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 による優れた正規表現など、負の数のサポートが必要な場合の解決策は他にもあります。