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?
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!"
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)
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?
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.
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
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.
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
PEP 3101 schlägt vor, den Operator %
durch die neue, erweiterte String-Formatierung in Python 3 zu ersetzen, wobei dies die Standardeinstellung wäre.
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')
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.
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.
%
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.
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 .
%
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.
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)
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
Für Python-Version> = 3.6 (siehe PEP 498 )
s1='albha'
s2='beta'
f'{s1}{s2:>10}'
#output
'albha beta'
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
-----
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}'
>>>
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 .