web-dev-qa-db-ja.com

Python文字列フォーマット:%vs. .format

Python 2.6では、既存の%演算子とは少し異なる構文で str.format() メソッドが導入されました。どっちが良いですか。

  1. 以下はそれぞれの方法を使用しても同じ結果になります。違いは何ですか?

    #!/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!"
    
  2. さらに、Pythonで文字列のフォーマットはいつ行われるのですか?たとえば、ロギングレベルがHIGHに設定されている場合でも、次の%操作を実行するとヒットするでしょうか。そしてもしそうなら、これを回避する方法はありますか?

    log.debug("some debug info: %s" % some_info)
    
1237
NorthIsUp

あなたの最初の質問に答えるには... .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の例がずっときれいに見えます。

なぜあなたはそれを使わないのですか? 

  • それについて知らない(これを読む前に私に)
  • python 2.5と互換性があること

あなたの2番目の質問に答えるために、文字列フォーマットは他の操作と同時に起こります - 文字列フォーマット式が評価されるとき。そして、怠惰な言語ではないPythonは、関数を呼び出す前に式を評価します。そのため、log.debugの例では、式"some debug info: %s"%some_infoが最初に評価されます。 "some debug info: roflcopters are active"、その文字列はlog.debug()に渡されます。 

888
Claudiu

モジュロ演算子(%)でできないことがあります。

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
289
eyquem

Pythonのloggingモジュールを使用していると仮定すると、フォーマットを自分で行うのではなく、文字列フォーマットの引数を.debug()メソッドの引数として渡すことができます。

log.debug("some debug info: %s", some_info)

ロガーが実際に何かをログに記録しない限り、これはフォーマットをすることを避けます。

132
Wooble

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 を参照してください。

98
Colonel Panic

PEP 3101 はPython 3で%演算子を新しい高度な文字列フォーマットに置き換えることを提案しています。これがデフォルトです。

55
BrainStorm

しかし、注意してください、ちょうど今、既存のコードのすべての%.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'

または引数文字列を文字列に変換できる場合(いわゆる「バイト配列」)

52
rslnx

.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'

私の言う限りでは、これは%では不可能です。

33
matiasg

私が今日発見したように、%を介して文字列をフォーマットする古い方法は、そのままでは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()メソッドをすぐに使用することをまだ検討するかもしれません。 

28
balu

%は私のテストの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。

25
lcltj

ちなみに、ロギングで新しいスタイルのフォーマットを使用するためにパフォーマンスを犠牲にする必要はありません。 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

14
David Sanders

%が役に立つかもしれない状況の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つの正規表現があり、一方がフォーマットなしで単独で使用されているが、両方の連結がフォーマットされている場合に便利です。

8
Jorge Leitão

あなたの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)
6
zhengcao

私はバージョン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")}')

例えば与える

こんにちは、ここに日付があります。

4

Pythonのバージョンが3.6以上の場合( PEP 498 を参照)

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'
2
Roushan

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
-----
1
Felix Martinez

ただし、中括弧を入れ子にしていると、フォーマットでは機能しませんが%は機能します。

例:

>>> '{{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}'
>>> 
1
U9-Forward

厳密に見てみると、元のトピックから実際には程遠いものになっています。 

Gettextモジュールを使用してローカライズされたGUIでは、新旧のスタイル文字列が唯一の方法です。 f文字列はそこでは使用できません。私見新しいスタイルは、この場合に最適です。これにSO質問があります ここ

0
jake77