Python 2.6では、既存の%
演算子とは少し異なる構文で str.format()
メソッドが導入されました。どっちが良いですか。
以下はそれぞれの方法を使用しても同じ結果になります。違いは何ですか?
#!/usr/bin/python
sub1 = "python string!"
sub2 = "an arg"
a = "i am a %s" % sub1
b = "i am a {0}".format(sub1)
c = "with %(kwarg)s!" % {'kwarg':sub2}
d = "with {kwarg}!".format(kwarg=sub2)
print a # "i am a python string!"
print b # "i am a python string!"
print c # "with an arg!"
print d # "with an arg!"
さらに、Pythonで文字列のフォーマットはいつ行われるのですか?たとえば、ロギングレベルがHIGHに設定されている場合でも、次の%
操作を実行するとヒットするでしょうか。そしてもしそうなら、これを回避する方法はありますか?
log.debug("some debug info: %s" % some_info)
あなたの最初の質問に答えるには... .format
はさまざまな意味でもっと洗練されているようです。 %
についての厄介なことは、それがどのように変数またはTupleを取ることができるかということです。あなたは以下が常にうまくいくと思います:
"hi there %s" % name
しかし、name
が偶然(1, 2, 3)
である場合、それはTypeError
をスローします。それが常に印刷されることを保証するために、あなたはする必要があるでしょう
"hi there %s" % (name,) # supply the single argument as a single-item Tuple
それはただ醜いです。 .format
にはこれらの問題はありません。また、2番目の例では、.format
の例がずっときれいに見えます。
なぜあなたはそれを使わないのですか?
あなたの2番目の質問に答えるために、文字列フォーマットは他の操作と同時に起こります - 文字列フォーマット式が評価されるとき。そして、怠惰な言語ではないPythonは、関数を呼び出す前に式を評価します。そのため、log.debug
の例では、式"some debug info: %s"%some_info
が最初に評価されます。 "some debug info: roflcopters are active"
、その文字列はlog.debug()
に渡されます。
モジュロ演算子(%)でできないことがあります。
tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)
結果
12 22222 45 22222 103 22222 6 22222
非常に便利。
もう一つのポイント:format()
は関数であるため、他の関数の引数として使用できます。
li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)
print
from datetime import datetime,timedelta
once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8, minutes=20)
gen =(once_upon_a_time +x*delta for x in xrange(20))
print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))
の結果:
['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']
2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00
Pythonのlogging
モジュールを使用していると仮定すると、フォーマットを自分で行うのではなく、文字列フォーマットの引数を.debug()
メソッドの引数として渡すことができます。
log.debug("some debug info: %s", some_info)
ロガーが実際に何かをログに記録しない限り、これはフォーマットをすることを避けます。
Python 3.6(2016)以降では、 f-strings を使って変数を置き換えることができます。
>>> Origin = "London"
>>> destination = "Paris"
>>> f"from {Origin} to {destination}"
'from London to Paris'
f"
プレフィックスに注意してください。 Python 3.5以前でこれを試すと、SyntaxError
が得られます。
https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings を参照してください。
PEP 3101 はPython 3で%
演算子を新しい高度な文字列フォーマットに置き換えることを提案しています。これがデフォルトです。
しかし、注意してください、ちょうど今、既存のコードのすべての%
を.format
に置き換えようとするときに、1つの問題を発見しました。'{}'.format(unicode_string)
このPythonインタラクティブセッションログを見てください:
Python 2.7.2 (default, Aug 27 2012, 19:52:55)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'
s
は単なる文字列(Python3では「バイト配列」と呼ばれる)であり、u
はUnicode文字列(Python3では「文字列」と呼ばれる)です。
; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'
Unicodeオブジェクトを%
演算子のパラメーターとして指定すると、元の文字列がUnicodeでなくてもUnicode文字列が生成されます。
; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)
ただし、.format
関数は「UnicodeEncodeError」を発生させます。
; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'
また、元の文字列がUnicodeである場合に限り、Unicode引数で正常に機能します。
; '{}'.format(u'i')
'i'
または引数文字列を文字列に変換できる場合(いわゆる「バイト配列」)
.format
のもう一つの利点(答えにはありません):それはオブジェクトのプロパティを取ります。
In [12]: class A(object):
....: def __init__(self, x, y):
....: self.x = x
....: self.y = y
....:
In [13]: a = A(2,3)
In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'
または、キーワード引数として:
In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'
私の言う限りでは、これは%
では不可能です。
私が今日発見したように、%
を介して文字列をフォーマットする古い方法は、そのままでは10進固定小数点および浮動小数点演算用のDecimal
モジュールをサポートしません。
例(Python 3.3.5を使用):
#!/usr/bin/env python3
from decimal import *
getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard
print('%.50f' % d)
print('{0:.50f}'.format(d))
出力:
0.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000
確かに回避策があるかもしれませんが、format()
メソッドをすぐに使用することをまだ検討するかもしれません。
%
は私のテストのformat
より良いパフォーマンスを与えます。
テストコード:
Python 2.7.2:
import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")
結果:
> format: 0.470329046249
> %: 0.357107877731
Python 3.5.2
import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))
結果
> format: 0.5864730989560485
> %: 0.013593495357781649
Python 2では見えますが、その違いはわずかですが、Python 3では%
はformat
よりはるかに高速です。
サンプルコードをくれてありがとう@Chris Cogdon。
ちなみに、ロギングで新しいスタイルのフォーマットを使用するためにパフォーマンスを犠牲にする必要はありません。 logging.debug
マジックメソッドを実装する任意のオブジェクトをlogging.info
、__str__
などに渡すことができます。 loggingモジュールが自分のメッセージオブジェクトを発行する必要があると判断した場合(それが何であれ)、それを実行する前にstr(message_object)
を呼び出します。だからあなたはこのようなことをすることができます:
import logging
class NewStyleLogMessage(object):
def __init__(self, message, *args, **kwargs):
self.message = message
self.args = args
self.kwargs = kwargs
def __str__(self):
args = (i() if callable(i) else i for i in self.args)
kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())
return self.message.format(*args, **kwargs)
N = NewStyleLogMessage
# Neither one of these messages are formatted (or calculated) until they're
# needed
# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))
def expensive_func():
# Do something that takes a long time...
return 'foo'
# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))
これはすべてPython 3のドキュメント( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles )に記載されています。ただし、Python 2.6でも動作します( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages )。
このスタイルを使用することの利点の1つは、それがフォーマットスタイルにとらわれないという事実以外に、それが遅延値を許容するということです。上記の関数expensive_func
。これは、Pythonのドキュメントで与えられているアドバイスに代わる、より洗練された方法を提供します。 https://docs.python.org/2.6/library/logging.html#optimization 。
%
が役に立つかもしれない状況の1つは、正規表現をフォーマットしているときです。例えば、
'{type_names} [a-z]{2}'.format(type_names='triangle|square')
IndexError
を発生させます。この状況では、あなたは使用することができます:
'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}
これは正規表現を'{type_names} [a-z]{{2}}'
として書くことを避けます。これは、2つの正規表現があり、一方がフォーマットなしで単独で使用されているが、両方の連結がフォーマットされている場合に便利です。
あなたのPython> = 3.6の場合、F文字列フォーマットのリテラルがあなたの新しい友達です。
それはより単純で、きれいで、そしてより良いパフォーマンスです。
In [1]: params=['Hello', 'adam', 42]
In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
私はバージョン3.6以降、私たちは以下のようにfstringsを使うことができると付け加えます。
foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")
どちらを与える
私の名前はジョン・スミスです
すべてが文字列に変換されます
mylist = ["foo", "bar"]
print(f"mylist = {mylist}")
結果:
mylist = ['foo'、 'bar']
他のフォーマットの方法のように、関数を渡すことができます
print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')
例えば与える
こんにちは、ここに日付があります。
Pythonのバージョンが3.6以上の場合( PEP 498 を参照)
s1='albha'
s2='beta'
f'{s1}{s2:>10}'
#output
'albha beta'
Python 3.6.7の比較:
#!/usr/bin/env python
import timeit
def time_it(fn):
"""
Measure time of execution of a function
"""
def wrapper(*args, **kwargs):
t0 = timeit.default_timer()
fn(*args, **kwargs)
t1 = timeit.default_timer()
print("{0:.10f} seconds".format(t1 - t0))
return wrapper
@time_it
def new_new_format(s):
print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")
@time_it
def new_format(s):
print("new_format:", "{0} {1} {2} {3} {4}".format(*s))
@time_it
def old_format(s):
print("old_format:", "%s %s %s %s %s" % s)
def main():
samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),)
for s in samples:
new_new_format(s)
new_format(s)
old_format(s)
print("-----")
if __== '__main__':
main()
出力:
new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----
ただし、中括弧を入れ子にしていると、フォーマットでは機能しませんが%
は機能します。
例:
>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
'{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>>
厳密に見てみると、元のトピックから実際には程遠いものになっています。
Gettextモジュールを使用してローカライズされたGUIでは、新旧のスタイル文字列が唯一の方法です。 f文字列はそこでは使用できません。私見新しいスタイルは、この場合に最適です。これにSO質問があります ここ 。