Python 2.6 wprowadził str.format()
metodę z nieco inną składnią niż istniejący %
operator. Które jest lepsze i na jakie sytuacje?
Poniżej zastosowano każdą metodę i ma ten sam wynik, więc jaka jest różnica?
#!/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!"
Ponadto, kiedy formatowanie łańcucha występuje w Pythonie? Na przykład, jeśli mój poziom rejestrowania jest ustawiony na WYSOKI, czy nadal będę otrzymywać trafienie za wykonanie następującej
%
operacji? A jeśli tak, to czy można tego uniknąć?log.debug("some debug info: %s" % some_info)
python
performance
logging
string-formatting
f-string
NorthIsUp
źródło
źródło
%
częściej używam starszego stylu, ponieważ jeśli nie potrzebujesz ulepszonych możliwościformat()
stylu,%
styl jest często o wiele wygodniejszy.format()
stylu formatowania i starszego%
stylu formatowania .Odpowiedzi:
Odpowiedź na twoje pierwsze pytanie ...
.format
wydaje się bardziej skomplikowana na wiele sposobów. Irytujące%
jest także to, jak może przyjmować zmienną lub krotkę. Można by pomyśleć, że zawsze będą działać następujące rzeczy:ale jeśli
name
tak się stanie(1, 2, 3)
, to rzuciTypeError
. Aby zagwarantować, że zawsze drukuje, musisz to zrobićco jest po prostu brzydkie.
.format
nie ma tych problemów. Również w drugim podanym przez ciebie.format
przykładzie przykład jest znacznie bardziej przejrzysty.Dlaczego tego nie używałbyś?
Aby odpowiedzieć na twoje drugie pytanie, formatowanie łańcucha odbywa się w tym samym czasie, co każda inna operacja - gdy oceniane jest wyrażenie formatujące łańcuch. Python, nie będąc leniwym językiem, ocenia wyrażenia przed wywołaniem funkcji, więc w twoim
log.debug
przykładzie wyrażenie"some debug info: %s"%some_info
najpierw oceni, na przykład"some debug info: roflcopters are active"
, a następnie ten ciąg zostanie przekazanylog.debug()
.źródło
"%(a)s, %(a)s" % {'a':'test'}
log.debug("something: %s" % x)
ale nie nalog.debug("something: %s", x)
Formatowanie łańcucha zostanie obsłużone w metodzie i nie dostaniesz spadku wydajności, jeśli nie zostanie zarejestrowany. Jak zawsze Python przewiduje twoje potrzeby =)'{0}, {0}'.format('test')
.man sprintf
i dowiedz się o$
notacji w%
printf("%2$d", 1, 3)
„3” jest określone w POSIX, a nie w C99. Sama strona podręcznika, do której się odnosiłeś, zauważa: „Standard C99 nie zawiera stylu używającego„ $ ”…”.Coś, czego operator modulo (%) nie może zrobić, afaik:
wynik
Bardzo przydatne.
Kolejna kwestia:
format()
będąc funkcją, może być używana jako argument w innych funkcjach:Prowadzi do:
źródło
map
tak łatwo, jak formatowania.map('some_format_string_%s'.__mod__, some_iterable)
printf("%2$s %1$s\n", "One", "Two");
skompilowanygcc -std=c99 test.c -o test
, wynikiem jestTwo One
. Ale poprawiłem się: to właściwie rozszerzenie POSIX, a nie C. Nie mogę go znaleźć ponownie w standardzie C / C ++, gdzie myślałem, że go widziałem. Kod działa nawet z flagą std „c90”.sprintf
strona man . Nie ma go na liście, ale pozwala libsowi zaimplementować nadzbiór. Mój oryginalny argument jest nadal aktualny, zastępującC
goPosix
%
do zmiany kolejności symboli zastępczych. Nadal chciałbym nie usuwać tego pierwszego komentarza ze względu na spójność komentarzy tutaj. Przepraszam, że wyładowałem tutaj swój gniew. Jest skierowany przeciwko często składanemu stwierdzeniu, że stara składnia per se nie pozwala na to. Zamiast tworzyć zupełnie nową składnię, moglibyśmy wprowadzić rozszerzenia std Posix. Moglibyśmy mieć jedno i drugie.Zakładając, że używasz
logging
modułu Pythona , możesz przekazać argumenty formatujące ciąg znaków jako argumenty do.debug()
metody zamiast samodzielnie formatować:co pozwala uniknąć formatowania, chyba że program rejestrujący coś faktycznie rejestruje.
źródło
log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry'))
Nie możesz jednak użyć tutaj nowej.format()
składni stylu , nawet w Pythonie 3.3, co jest wstydem.Począwszy od wersji Python 3.6 (2016) można używać ciągów F w celu podstawiania zmiennych:
Zwróć uwagę na
f"
prefiks. Jeśli spróbujesz tego w Pythonie 3.5 lub wcześniejszym, otrzymaszSyntaxError
.Zobacz https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings
źródło
PEP 3101 proponuje zastąpienie
%
operatora nowym, zaawansowanym formatowaniem napisów w Pythonie 3, gdzie byłoby to ustawienie domyślne.źródło
.format
nie zastąpi%
formatowania łańcucha.Ale proszę uważać, dopiero teraz odkryłem jeden problem, gdy próbuje zastąpić wszystko
%
ze.format
w istniejący kod:'{}'.format(unicode_string)
będzie próbował zakodować unicode_string i prawdopodobnie nie uda.Wystarczy spojrzeć na ten interaktywny dziennik sesji Pythona:
s
jest tylko łańcuchem (zwanym „tablicą bajtów” w Python3) iu
łańcuchem Unicode (zwanym „łańcuchem” w Python3):Gdy podasz
%
operatorowi obiekt Unicode jako parametr , wygeneruje on ciąg Unicode, nawet jeśli oryginalny ciąg nie był Unicode:ale
.format
funkcja wywoła „UnicodeEncodeError”:i będzie działał z argumentem Unicode w porządku, tylko jeśli oryginalny ciąg znaków był Unicode.
lub jeśli ciąg argumentu można przekonwertować na ciąg znaków (tak zwana „tablica bajtów”)
źródło
format
metody są naprawdę potrzebne ...%
interpolacja łańcuchów kiedykolwiek zniknęła."p1=%s p2=%d" % "abc", 2
lub"p1=%s p2=%s" % (tuple_p1_p2,)
. Możesz pomyśleć, że to wina programisty, ale myślę, że to po prostu dziwna, błędna składnia, która wygląda ładnie dla szybkiego skryptu, ale źle wpływa na kod produkcyjny.%s
,%02d
jak"p1=%s p2=%02d".format("abc", 2)
. Obwiniam tych, którzy wymyślili i zatwierdzili formatowanie nawiasów klamrowych, które wymaga od ciebie ucieczki{{}}
i wygląda brzydko imho.Kolejna zaleta
.format
(której nie widzę w odpowiedziach): może przyjmować właściwości obiektu.Lub jako argument słowa kluczowego:
O
%
ile mi wiadomo, nie jest to możliwe .źródło
'x is {0}, y is {1}'.format(a.x, a.y)
. Powinien być stosowany tylko wtedy, gdya.x
operacja jest bardzo kosztowna.'x is {a.x}, y is {a.y}'.format(a=a)
. Bardziej czytelny niż oba przykłady.'x is {a.x}, y is {a.y}'.format(**vars())
'{foo[bar]}'.format(foo={'bar': 'baz'})
.Your order, number {order[number]} was processed at {now:%Y-%m-%d %H:%M:%S}, will be ready at about {order[eta]:%H:%M:%S}
lub cokolwiek zechce. Jest to o wiele czystsze niż próba oferowania tej samej funkcjonalności ze starym formatyzatorem. To sprawia, że ciągi formatu dostarczone przez użytkownika są o wiele potężniejsze.%
daje lepszą wydajność niżformat
z mojego testu.Kod testowy:
Python 2.7.2:
Wynik:
Python 3.5.2
Wynik
Wygląda w Python2, różnica jest niewielka, podczas gdy w Python3
%
jest znacznie szybsza niżformat
.Dzięki @Chris Cogdon za przykładowy kod.
Edycja 1:
Testowany ponownie w Pythonie 3.7.2 w lipcu 2019 r.
Wynik:
Nie ma dużej różnicy. Myślę, że Python stopniowo się poprawia.
Edycja 2:
Po tym, jak ktoś wspomniał w komentarzu o łańcuchu f Pythona 3, zrobiłem test dla następującego kodu w Pythonie 3.7.2:
Wynik:
Wydaje się, że f-string jest wciąż wolniejszy niż,
%
ale lepszy niżformat
.źródło
str.format
daje więcej funkcji (zwłaszcza np'{0:%Y-%m-%d}'.format(datetime.datetime.utcnow())
. Formatowanie specjalistyczne ). Wydajność nie może być bezwzględnym wymogiem wszystkich zadań. Użyj odpowiedniego narzędzia do pracy.%
operator pozwala ponownie wykorzystaćprintf
wiedzę; interpolacja słownikowa jest bardzo prostym rozszerzeniem zasady.Jak dzisiaj odkryłem, stary sposób formatowania napisów za pomocą
%
nie obsługujeDecimal
modułu Pythona dla dziesiętnych stałych i arytmetyki zmiennoprzecinkowej, po wyjęciu z pudełka.Przykład (przy użyciu Python 3.3.5):
Wynik:
Z pewnością mogą istnieć obejścia, ale nadal możesz rozważyć użycie tej
format()
metody od razu.źródło
str(d)
przed rozwinięciem parametru, podczas gdy formatowanie w starym stylu prawdopodobnie wywołujefloat(d)
najpierw.str(d)
wraca"3.12375239e-24"
, nie"0.00000000000000000000000312375239000000000000000000"
Jeśli twój python> = 3.6, literał sformatowany w ciąg F jest twoim nowym przyjacielem.
To jest prostsze, czyste i lepsza wydajność.
źródło
Na marginesie, nie musisz naciskać na wydajność, aby używać formatowania w nowym stylu z logowaniem. Możesz przejść do dowolnego obiektu
logging.debug
,logging.info
itp, który implementuje__str__
metody magiczne. Gdy moduł rejestrujący zdecyduje, że musi wyemitować obiekt wiadomości (cokolwiek to jest), wywołuje go,str(message_object)
zanim to zrobi. Więc możesz zrobić coś takiego:Wszystko to opisano w dokumentacji Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Będzie jednak działać również z Pythonem 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).
Jedną z zalet korzystania z tej techniki, poza faktem, że jest ona agnostyczna w stylu formatowania, jest to, że pozwala ona na leniwe wartości, np
expensive_func
. Powyższą funkcję . Zapewnia to bardziej elegancką alternatywę dla porad zawartych w dokumentach Pythona tutaj: https://docs.python.org/2.6/library/logging.html#optimization .źródło
format
bez spadku wydajności - robi to poprzez przesłonięcie__str__
dokładnie tak, jaklogging
zostało zaprojektowane - skraca wywołanie funkcji do pojedynczej litery (N
), która jest bardzo podobna do niektórych standardowych sposobów definiowania ciągów - I pozwala na leniwe wywołanie funkcji. Dziękuję Ci! +1logging.Formatter(style='{')
parametru?Jedną z sytuacji, w której
%
może pomóc, jest formatowanie wyrażeń regularnych. Na przykład,podnosi
IndexError
. W tej sytuacji możesz użyć:Pozwala to uniknąć pisania wyrażenia regularnego jako
'{type_names} [a-z]{{2}}'
. Może to być przydatne, gdy masz dwa wyrażenia regularne, z których jeden jest używany sam bez formatu, ale połączenie obu jest sformatowane.źródło
'{type_names} [a-z]{{2}}'.format(type_names='triangle|square')
. To tak, jakby powiedzieć, że.format()
może pomóc, gdy używa się ciągów, które już zawierają znak procentu. Pewnie. Musisz wtedy uciec."One situation where % may help is when you are formatting regex expressions."
Konkretnie, załóżmy, żea=r"[a-z]{2}"
jest to fragment wyrażenia regularnego, że będziesz użyty w dwóch różnych wyrażeniach końcowych (np.c1 = b + a
Ic2 = a
). Załóżmy, żec1
należy goformat
edytować (np.b
Należy sformatować środowisko wykonawcze), alec2
nie robi tego. Następnie trzebaa=r"[a-z]{2}"
nac2
ia=r"[a-z]{{2}}"
nac1.format(...)
.Dodałbym, że od wersji 3.6 możemy używać ciągów takich jak poniżej
Które dają
Wszystko jest konwertowane na ciągi
Wynik:
możesz przekazać funkcję, jak w metodzie innych formatów
Dając na przykład
źródło
Dla wersji python> = 3.6 (patrz PEP 498 )
źródło
Porównywalny język Python 3.6.7:
Wynik:
źródło
Ale jedno jest takie, że jeśli masz zagnieżdżone nawiasy klamrowe, nie będą działać dla formatu, ale
%
będą działać.Przykład:
źródło