it-swarm.com.de

Python-String-Formatierung:% vs. .format

Python 2.6 führte die str.format() -Methode mit einer etwas anderen Syntax ein als der vorhandene %-Operator. Welches ist besser und für welche Situationen?

  1. Im Folgenden wird jede Methode verwendet und das gleiche Ergebnis erzielt. Was ist also der Unterschied?

    #!/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. Wann kommt es außerdem zu einer String-Formatierung in Python? Wenn meine Protokollierungsstufe beispielsweise auf HIGH gesetzt ist, erhalte ich trotzdem einen Treffer für die Ausführung der folgenden %-Operation? Und wenn ja, gibt es eine Möglichkeit, dies zu vermeiden?

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

Um Ihre erste Frage zu beantworten ... scheint .format in vielerlei Hinsicht anspruchsvoller zu sein. Eine ärgerliche Sache bei % ist auch, wie eine Variable oder ein Tuple verwendet werden kann. Sie würden denken, dass Folgendes immer funktionieren würde:

"hi there %s" % name

falls jedoch name zufällig (1, 2, 3) ist, wird eine TypeError ausgegeben. Um zu garantieren, dass immer gedruckt wird, müssen Sie dies tun

"hi there %s" % (name,)   # supply the single argument as a single-item Tuple

das ist einfach hässlich. .format hat diese Probleme nicht. Auch in dem zweiten Beispiel, das Sie angegeben haben, sieht das .format-Beispiel viel sauberer aus.

Warum würdest du es nicht benutzen? 

  • ich weiß es nicht (ich bevor ich das lese)
  • kompatibel mit Python 2.5

Um Ihre zweite Frage zu beantworten, erfolgt die Formatierung von Zeichenfolgen gleichzeitig mit jeder anderen Operation - wenn der Ausdruck für die Formatierung von Zeichenfolgen ausgewertet wird. Und da Python keine faule Sprache ist, wertet er Ausdrücke aus, bevor Funktionen aufgerufen werden. In Ihrem log.debug-Beispiel wird der Ausdruck "some debug info: %s"%some_info zuerst ausgewertet, z. "some debug info: roflcopters are active", dann wird diese Zeichenfolge an log.debug() übergeben. 

888
Claudiu

Das kann der Modulo-Operator (%) nicht, afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

ergebnis

12 22222 45 22222 103 22222 6 22222

Sehr hilfreich.

Ein weiterer Punkt: format() kann als Funktion in anderen Funktionen verwendet werden: 

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))

Ergebnisse in:

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

Wenn Sie das logging-Modul von Python verwenden, können Sie die Argumente für die Formatierung der Zeichenfolge als Argumente an die .debug()-Methode übergeben, anstatt die Formatierung selbst durchzuführen:

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

dies vermeidet die Formatierung, es sei denn, der Logger protokolliert tatsächlich etwas.

132
Wooble

Ab Python 3.6 (2016) können Sie f-strings verwenden, um Variablen zu ersetzen:

>>> Origin = "London"
>>> destination = "Paris"
>>> f"from {Origin} to {destination}"
'from London to Paris'

Beachten Sie das Präfix f". Wenn Sie dies in Python 3.5 oder früher versuchen, erhalten Sie eine SyntaxError.

Siehe https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

98
Colonel Panic

PEP 3101 schlägt vor, den Operator % durch die neue, erweiterte String-Formatierung in Python 3 zu ersetzen, wobei dies die Standardeinstellung wäre.

55
BrainStorm

Aber seien Sie vorsichtig, gerade jetzt habe ich ein Problem entdeckt, als ich versuchte, alle % durch .format in vorhandenem Code zu ersetzen:'{}'.format(unicode_string) wird versuchen, unicode_string zu codieren, und schlägt wahrscheinlich fehl.

Schauen Sie sich dieses interaktive Python-Sitzungsprotokoll an:

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 ist nur eine Zeichenfolge (in Python3 als "Byte-Array" bezeichnet) und u ist eine Unicode-Zeichenfolge (in Python3 als "Zeichenfolge" bezeichnet):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

Wenn Sie dem %-Operator ein Unicode-Objekt als Parameter übergeben, wird eine Unicode-Zeichenfolge erzeugt, auch wenn die ursprüngliche Zeichenfolge kein Unicode war:

; '{}'.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)

die .format-Funktion löst jedoch "UnicodeEncodeError" aus:

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

und es funktioniert mit einem Unicode-Argument nur dann, wenn die ursprüngliche Zeichenfolge Unicode war.

; '{}'.format(u'i')
'i'

oder wenn Argumentstring in einen String konvertiert werden kann (sogenanntes 'Byte-Array')

51
rslnx

Ein weiterer Vorteil von .format (den ich in den Antworten nicht sehe): Es kann Objekteigenschaften annehmen.

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'

Oder als Keyword-Argument:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

Dies ist mit % nicht möglich, soweit ich das beurteilen kann.

33
matiasg

Wie ich heute festgestellt habe, unterstützt die alte Methode der Formatierung von Zeichenfolgen über %Decimal, Python's Modul für dezimale Festkomma- und Fließkomma-Arithmetik, nicht standardmäßig.

Beispiel (mit 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))

Ausgabe:

0.00000000000000000000000312375239000000009907464850 0.000000000000000000000031237523900000000000000000000

Es kann sicherlich Umgehungen geben, Sie können jedoch trotzdem sofort die format()-Methode verwenden. 

28
balu

% bietet eine bessere Leistung als format aus meinem Test.

Testcode:

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')")

Ergebnis:

> 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')"))

Ergebnis

> format: 0.5864730989560485
> %: 0.013593495357781649

Es sieht in Python2 aus, der Unterschied ist klein, wohingegen % in Python3 viel schneller ist als format.

Danke @Chris Cogdon für den Beispielcode.

25
lcltj

Als Nebenbemerkung müssen Sie keinen Performance-Hit hinnehmen, um die neue Formatierung mit der Protokollierung zu verwenden. Sie können jedes Objekt an logging.debug, logging.info usw. übergeben, das die magische Methode __str__ implementiert. Wenn das Protokollierungsmodul entschieden hat, dass es Ihr Nachrichtenobjekt ausgeben muss (was auch immer es ist), ruft es str(message_object) auf, bevor es ausgeführt wird. So könnte man so etwas machen:

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))

Dies ist alles in der Python 3-Dokumentation beschrieben ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Es funktioniert jedoch auch mit Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).

Einer der Vorteile der Verwendung dieser Technik ist, abgesehen von der Tatsache, dass sie agnostisch im Formatierungsstil ist, die Möglichkeit, faule Werte, z. die Funktion expensive_func oben. Dies bietet eine elegantere Alternative zu den Ratschlägen in den Python-Dokumenten hier: https://docs.python.org/2.6/library/logging.html#optimization .

14
David Sanders

% kann hilfreich sein, wenn Sie Regex-Ausdrücke formatieren. Zum Beispiel, 

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

wirft IndexError auf. In dieser Situation können Sie Folgendes verwenden:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

Dadurch wird vermieden, dass der Regex als '{type_names} [a-z]{{2}}' geschrieben wird. Dies kann nützlich sein, wenn Sie zwei reguläre Ausdrücke haben, von denen eine allein ohne Format verwendet wird, die Verkettung beider jedoch formatiert ist.

8
Jorge Leitão

Wenn Ihr Python> = 3.6, ist das F-String-formatierte Literal Ihr neuer Freund.

Es ist einfacher, sauberer und eine bessere Leistung.

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

Ich würde hinzufügen, dass wir seit Version 3.6 fstrings wie folgt verwenden können

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

Welche geben

Ich heiße John Smith

Alles wird in Strings konvertiert

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

Ergebnis:

mylist = ['foo', 'bar']

Sie können Funktion übergeben, wie in anderen Formaten Methode

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

Zum Beispiel geben

Hallo, hier ist der Termin: 16.04.2014

4

Für Python-Version> = 3.6 (siehe PEP 498 )

s1='albha'
s2='beta'

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

#output
'albha      beta'
2
Roushan

Python 3.6.7 vergleichend:

#!/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()

Ausgabe:

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

Aber eine Sache ist, dass auch, wenn Sie geschweifte Klammern geschachtelt haben, das Format nicht funktioniert, % jedoch funktioniert.

Beispiel:

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

Streng gesehen kommen wir wirklich weit vom ursprünglichen Thema entfernt, aber warum dann nicht: 

Wenn Sie das gettext-Modul verwenden, um z. Eine lokalisierte GUI, alte und neue Zeichenfolgen sind der einzige Weg. F-Strings können dort nicht verwendet werden. IMHO das neue Design ist die beste Wahl für diesen Fall. Es gibt eine SO Frage zu diesem hier .

0
jake77