1448

There are various string formatting methods:

  • Python <2.6: "Hello %s" % name
  • Python 2.6+: "Hello {}".format(name)   (uses str.format)
  • Python 3.6+: f"{name}"   (uses f-strings)

Which is better, and for what situations?


  1. The following methods have the same outcome, so what is the difference?

    name = "Alice"
    
    "Hello %s" % name
    "Hello {0}".format(name)
    f"Hello {name}"
    
    # Using named arguments:
    "Hello %(kwarg)s" % {'kwarg': name}
    "Hello {kwarg}".format(kwarg=name)
    f"Hello {name}"
    
  2. When does string formatting run, and how do I avoid a runtime performance penalty?


If you are trying to close a duplicate question that is just looking for a way to format a string, please use How do I put a variable’s value inside a string?.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
NorthIsUp
  • 17,502
  • 9
  • 29
  • 35
  • 2
    similar to http://stackoverflow.com/questions/3691975/s-format-vs-0-format-vs-format – carl Feb 22 '11 at 18:50
  • 3
    For beginners: Here is a [very nice tutorial](http://www.python-course.eu/python3_formatted_output.php) that teaches both styles. I personally use the older ``%`` style more often, because if you do not need the improved capabilities of the ``format()`` style, the ``%`` style is often a lot more convenient. – Lutz Prechelt Apr 12 '16 at 09:24
  • 2
    For reference: Python 3 documentation for the [newer ``format()`` formatting style](https://docs.python.org/3/library/string.html#format-string-syntax) and the [older ``%``-based formatting style](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting). – Lutz Prechelt Apr 12 '16 at 09:26
  • 3
    See also: [Pythons many ways of string formatting](http://stackoverflow.com/questions/13451989/pythons-many-ways-of-string-formatting-are-the-older-ones-going-to-be-deprec) – gerrit Jan 06 '17 at 09:39
  • 1
    To answer your second question, since 3.2 you can use {} format if you use a custom formatter (see https://docs.python.org/3/library/logging.html#logging.Formatter) – yanjost Sep 20 '17 at 21:36
  • 1
    The website https://pyformat.info has a side by side comparison of features, and serves as a convenient reference for those less frequently used features! – Charles Oct 18 '17 at 20:17
  • **For duplicate closers**: See also https://stackoverflow.com/questions/2960772 as a target for questions that are simply asking for a way to do it. That one answers the question more directly. This question is explicilty about the pros and cons of the various approaches. – Karl Knechtel Jul 27 '22 at 03:55

16 Answers16

986

To answer your first question... .format just seems more sophisticated in many ways. An annoying thing about % is also how it can either take a variable or a tuple. You'd think the following would always work:

"Hello %s" % name

yet, if name happens to be (1, 2, 3), it will throw a TypeError. To guarantee that it always prints, you'd need to do

"Hello %s" % (name,)   # supply the single argument as a single-item tuple

which is just ugly. .format doesn't have those issues. Also in the second example you gave, the .format example is much cleaner looking.

Only use it for backwards compatibility with Python 2.5.


To answer your second question, string formatting happens at the same time as any other operation - when the string formatting expression is evaluated. And Python, not being a lazy language, evaluates expressions before calling functions, so the expression log.debug("some debug info: %s" % some_info) will first evaluate the string to, e.g. "some debug info: roflcopters are active", then that string will be passed to log.debug().

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 116
    what about `"%(a)s, %(a)s" % {'a':'test'}` – ted Aug 23 '12 at 09:53
  • 134
    Note that you will waste time for `log.debug("something: %s" % x)` but not for `log.debug("something: %s", x)` The string formatting will be handled in the method and you won't get the performance hit if it won't be logged. As always, Python anticipates your needs =) – darkfeline Dec 14 '12 at 23:13
  • String interpolation via the % character is being phased out from the look of it. The current [PEP](http://www.python.org/dev/peps/pep-3101/) suggests it will be around for a while, but I would count more on .format in the future. – meawoppl Jan 26 '13 at 05:23
  • 68
    ted: that’s a worse-looking hack to do the same as `'{0}, {0}'.format('test')`. – flying sheep Jan 30 '13 at 20:43
  • 19
    The point is: The one recurring argument that the new syntax allows reordering of items is a moot point: You can do the same with the old syntax. Most people do not know that this is actually already defined in the Ansi C99 Std! Check out a recent copy of `man sprintf` and learn about the `$` notation inside `%` placeholders – cfi Feb 20 '13 at 12:42
  • 29
    @cfi: If you mean something like, `printf("%2$d", 1, 3)` to print out "3", that's specified in POSIX, not C99. The very man page you referenced notes, "The C99 standard does not include the style using '$'…". – Thanatos Mar 07 '13 at 23:55
  • 2
    Also, `"%d atoms" % 10*3` gives "10 atoms10 atoms10 atoms", But `10 atoms10 atoms10 atoms` gives "9 atoms" – Shijing Lv Jun 27 '13 at 21:10
  • @flyingsheep if you have ever looked through the Django source code this "hack" is used quite frequently. – Tom Jul 24 '13 at 15:29
  • 3
    Tom: if your intention is only to use the same object twice, not increasing clarity, it’s indeed a hack. if you’re somehow stuck with `%` (e.g. because it’s baked in your API), then you have to use it, of course. – flying sheep Jul 24 '13 at 16:53
  • 4
    .format may be more elegant but it can be up to 5 times slower than % which could be significant depending on your goals. – Hone Watson Jul 20 '14 at 23:01
  • 4
    I've actually had cases where old-style formatting was much slower compared to new-style formatting using index tokens. It probably depends on how you're using it. Also, `str.format` is more object-oriented since it's just a method on `str` instances instead of a special syntax. This is more in line with python's "explicit is better than implicit" philosophy since it eschews extra syntax. – David Sanders Oct 21 '14 at 19:40
  • 1
    @DavidSanders Using the `%` notation is exactly as OOP as using `.format()`, the percent-operator just being the `.__mod__()` method of string objects. – Alfe Mar 02 '17 at 15:55
  • 2
    the only disadvantage I can think for for .format is that it takes longer to type `"{0}".format(var)` than `"%s" % var` – Nick Aug 25 '17 at 02:35
  • I would love to say that .format is better to learn as it can do more things. But standard libraries such as logging, argparse etc. expect template strings to be in the %() format. – Sriram Sep 03 '19 at 08:06
  • I found a use case where string interpolation is still better. You can pass in a sparsely populated `defaultdict` to `%`, but there isn't a way to do it with `.format()`. Let's say you have: ``` a = defaultdict(lambda: 'N/A') a.update({ 'name' : 'Felix', 'species' : 'cat'}) b = defaultdict(lambda: 'N/A') b.update({ 'name' : 'Cookie Monster', 'favoriteFood' : 'cookies'}) a_format = 'name: {name}, species: {species}, food: {favoriteFood}' a_out = a_format.format(**a) # KeyError b_format = 'name: %(name)s, species: %(species)s, food: %(favoriteFood)s' % b # works! ``` – jontsai Dec 28 '19 at 16:30
  • When you are using python 3.6 or newer verion, I recommend f-string feature. FYI, if you want left brace or right brace within f-string, just doulbe it. ```>>> num=12``` ```>>> hex_num=f'{num} in hex is {num:04X}'``` ```>>> print(f'i want to print brace literal : {{{hex_num}}} ')``` The result is ```i want to print brace literal : {12 in hex is 000C} ``` – Hongsoog Aug 19 '20 at 03:20
  • Why does pylint says to use % (lazy init) instead of others? – Verthais Feb 09 '22 at 07:53
319

Something that the modulo operator ( % ) can't do, afaik:

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

result

12 22222 45 22222 103 22222 6 22222

Very useful.

Another point: format(), being a function, can be used as an argument in other functions:

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

Results 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
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
eyquem
  • 26,771
  • 7
  • 38
  • 46
  • 19
    You can use old style formatting in `map` just as easily as format. `map('some_format_string_%s'.__mod__, some_iterable)` – agf Nov 28 '12 at 05:49
  • This is wrong. See other answers, and have a look at the original definition for the old syntax. Most people do not know that this is actually already defined in the Ansi C99 Std! Check out a recent copy of `man sprintf` and learn about the `$` notation inside `%` placeholders. So even _if_ Python would not support this feature of the original syntax, why introduce a new syntax? Why not just extend the interpreter to be able to understand all of what's in the C99 std? – cfi Feb 20 '13 at 12:44
  • 3
    @cfi: please prove you are right by rewriting the example above in C99 – MarcH Feb 15 '14 at 14:11
  • 10
    @MarcH: `printf("%2$s %1$s\n", "One", "Two");` compiled with `gcc -std=c99 test.c -o test`, the output is `Two One`. But I stand corrected: [It is actually a POSIX extension](http://stackoverflow.com/a/6322594/923794) and not C. I cannot find it again in the C/C++ standard, where I thought I'd seen it. The code works even with 'c90' std flag. [`sprintf` man page](http://linux.die.net/man/3/sprintf). [This](http://www.cplusplus.com/reference/cstdio/printf/#compatibility) does not list it, but allows libs to implement a superset. My original argument is still valid, replacing `C` with `Posix` – cfi Feb 15 '14 at 15:18
  • 8
    My first comment here, does not apply to this answer. I regret the phrasing. In Python we cannot use the modulo operator `%` for reordering placeholders. I'd still like to not delete that first comment for the sake of comment consistency here. I apologize for having vented my anger here. It is directed against the often made statement that the old syntax per se would not allow this. Instead of creating a completely new syntax we could have introduced the std Posix extensions. We could have both. – cfi Feb 15 '14 at 15:25
  • 19
    'modulo' refers to the operator that evaluates a remainder after a division. in this case the percent sign is not a modulo operator. – Octopus May 08 '14 at 21:09
  • @Octopus Semantics is the only compelling argument against the old style operator so far in my mind. Eg, modulo and format concepts shouldn't have the same syntax. If only because I'm never sure if % is a modulus operators when all I use it for is string formatting. I wish .format wasn't so hideous. – ThorSummoner Apr 17 '15 at 19:20
  • (@cfi off-topic, but the `%2$s` thing is pretty broken anyway in C; for example you cannot skip an argument; and the format syntax is much more versatile than just the reordering thing) – Antti Haapala -- Слава Україні Apr 28 '16 at 05:58
  • 3
    @Octopus: from https://docs.python.org/release/2.5/lib/typesseq-strings.html "String and Unicode objects have one unique built-in operation: the % operator (modulo)." So, yes, it *is* the modulo operator, and it has the magic method `__mod__`, even if it does not have the mathematical semantics of modulo in this usage. – Jason S Feb 03 '17 at 15:25
  • whilst interesting there is no explanation as to what is actually going on in the top example of your answer. Appears to unpack by index. – QHarr Feb 06 '19 at 12:29
157

Assuming you're using Python's logging module, you can pass the string formatting arguments as arguments to the .debug() method rather than doing the formatting yourself:

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

which avoids doing the formatting unless the logger actually logs something.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Wooble
  • 87,717
  • 12
  • 108
  • 131
  • 10
    This is some useful info that I just learned now. It's a pity it doesn't have it's own question as it seems separate to the main question. Pity the OP didn't split his question in two separate questions. – snth Nov 14 '12 at 07:36
  • 12
    You can use dict formatting like this: `log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry'))` However, you can't use the new style `.format()` syntax here, not even in Python 3.3, which is a shame. – Cito Nov 25 '12 at 17:00
  • 15
    @Cito: See this: http://plumberjack.blogspot.co.uk/2010/10/supporting-alternative-formatting.html – Vinay Sajip Jan 30 '13 at 19:56
  • 2
    @VinaySajip: Cool, thanks for the info and sorry for spreading misinformation. It's also in the "What's new in 3.2" article here: http://docs.python.org/3.2/whatsnew/3.2.html#logging. Very useful. – Cito Jan 30 '13 at 21:55
  • @snth I just split the OP's question into two separate questions. But this good answer unifies both. – smci Mar 12 '13 at 10:17
  • 34
    The primary benefit of this is not performance (doing the string interpolation will be quick compared to whatever you're doing with the output from logging, e.g displaying in a terminal, saving to disk) It is that if you have a logging aggregator, it can tell you "you got 12 instances of this error message", even if they all had different 'some_info' values. If the string formatting is done before passing the string to log.debug, then this is impossible. The aggregator can only say "you had 12 different log messages" – Jonathan Hartley Oct 10 '13 at 08:04
  • 2
    @JonathanHartley: depends on how sophisticated your aggregator is. – Wooble Oct 10 '13 at 10:12
  • Hey @Wooble. I don't see how. Doing this is absolutely trivial (i.e. can be implemented in the aggregator in about three characters, literally), and if it doesn't do this, then in what sense is it a useful aggregator? – Jonathan Hartley Oct 10 '13 at 12:22
  • No, I meant that a sufficiently sophisticated system could just find the similarities and group them together even if you use string formatting. Not that I'm aware of whether any can actually do this. – Wooble Oct 10 '13 at 12:24
  • 1
    Oh! I see. I took your meaning backwards. Beg your pardon. Yep, I could imagine an aggregator that tried to do that, but it seems silly. Hugs! – Jonathan Hartley Oct 10 '13 at 12:26
  • 7
    If you're concerned about performance, use literal dict {} syntax instead of a dict() class instantiation: http://doughellmann.com/2012/11/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – trojjer Feb 14 '14 at 11:03
  • 1
    You can defer formatting with any kind of formatting style. You're not restricted to python modulo formatting. The logging module converts objects to strings before emitting log messages. Therefore, any object that implements `__str__` will do. That could include a class which accepts a new-style formatting string with its parameters but doesn't perform the operation until the logging module calls `__str__`. See my answer below. – David Sanders Oct 21 '14 at 19:49
135

As of Python 3.6 (2016) you can use f-strings to substitute variables:

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

Note the f" prefix. If you try this in Python 3.5 or earlier, you'll get a SyntaxError.

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

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 7
    This doesn't answer the question. Another answer that mentions f-strings at least talks about performance: https://stackoverflow.com/a/51167833/7851470 – Georgy Jun 17 '19 at 12:00
  • f-strings are cute, and remind me of Ruby syntax. But they don't seem to have a lot of advantages, and, as you've said, they unnecessarily break compatibility with Python < 3.6 – Eric Duminil Dec 16 '22 at 09:31
  • I just don't like putting expressions into strings which means searching for code is now hidden in strings and syntax errors are not discovered until run time. I think they're saying that f-strings are not any more susceptible to injection attacks but I'm not sure I believe them. – NeilG Apr 18 '23 at 02:55
64

PEP 3101 proposes the replacement of the % operator with the new, advanced string formatting in Python 3, where it would be the default.

Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
BrainStorm
  • 2,036
  • 1
  • 16
  • 23
  • 14
    Untrue: "Backwards compatibility can be maintained by leaving the existing mechanisms in place."; of course, `.format` won't _replace_ `%` string formatting. – Tobias Jan 28 '13 at 23:32
  • 15
    No, BrainStorms postulation is true: "intended as a replacement for the existing '%'". Tobias quote means both systems will coexist for some time. RTFPEP – phobie Aug 19 '15 at 15:00
60

But please be careful, just now I've discovered one issue when trying to replace all % with .format in existing code: '{}'.format(unicode_string) will try to encode unicode_string and will probably fail.

Just look at this Python interactive session log:

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 is just a string (called 'byte array' in Python3) and u is a Unicode string (called 'string' in Python3):

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

When you give a Unicode object as a parameter to % operator it will produce a Unicode string even if the original string wasn't 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)

but the .format function will raise "UnicodeEncodeError":

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

and it will work with a Unicode argument fine only if the original string was Unicode.

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

or if argument string can be converted to a string (so called 'byte array')

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
wobmene
  • 1,108
  • 9
  • 14
  • 13
    There is simply no reason to change working code unless the additional features of the new `format` method are really needed ... – Tobias Jan 28 '13 at 22:51
  • absolutely agree with you, Tobias, but sometimes it's needed when upgrading to newer versions of Python – wobmene Jan 30 '13 at 13:17
  • 3
    For instance? AFAIK, it has _never_ been needed; I don't consider it likely that the `%` string interpolation would ever go away. – Tobias Jan 31 '13 at 13:45
  • 4
    I consider .format() function safer than % for strings. Often I see beginners' mistakes like this `"p1=%s p2=%d" % "abc", 2` or `"p1=%s p2=%s" % (tuple_p1_p2,)`. You might think it's the coder's fault but I think it's just weird faulty syntax that looks nice for the quicky-scriptie but is bad for production code. – wobmene Jan 06 '14 at 15:07
  • 4
    But I don't like the syntax of .format(), I'd be happier with good old `%s`, `%02d` like `"p1=%s p2=%02d".format("abc", 2)`. I blame those who invented and approved the curly braces formatting that needs you to escape them like `{{}}` and looks ugly imho. – wobmene Jan 06 '14 at 15:15
  • When it comes to "safety" - those mistakes are easy to avoid, IMO, once the usage is understood. It is a good habit to always use a tuple (and wrap single strings or numbers into it) or a dict. – Tobias Jan 07 '14 at 11:00
  • Great point. I think it is reasonable to have to explicitly promote the source string to unicode if you have unicode arguments: ie. u'{0} I am unicode now {1}'.format(a_byte_str, a_unicode_str). – Sean Jul 31 '14 at 13:53
  • The problem @rslnx describes is no longer an issue. It only applies to python2.7, which is already end-of-life. The problem is that python 2 would implicitly change encodings. Python3 will (properly) report this as an error. If you really do want an encoding, you can opt-in by using `'foo'.encode('utf-8', errors='ignore')` – ninMonkey Aug 21 '19 at 10:25
47

% gives better performance than format from my test.

Test code:

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

Result:

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

Result

> format: 0.5864730989560485
> %: 0.013593495357781649

It looks in Python2, the difference is small whereas in Python3, % is much faster than format.

Thanks @Chris Cogdon for the sample code.

Edit 1:

Tested again in Python 3.7.2 in July 2019.

Result:

> format: 0.86600608
> %: 0.630180146

There is not much difference. I guess Python is improving gradually.

Edit 2:

After someone mentioned python 3's f-string in comment, I did a test for the following code under python 3.7.2 :

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))
print('f-string:', timeit.timeit("f'{1}{1.23}{\"hello\"}'"))

Result:

format: 0.8331376779999999
%: 0.6314778750000001
f-string: 0.766649943

It seems f-string is still slower than % but better than format.

lcltj
  • 1,578
  • 14
  • 13
  • 45
    Instead, `str.format` gives more functionalities (especially type-specialized formatting e.g. `'{0:%Y-%m-%d}'.format(datetime.datetime.utcnow())`). Performance cannot be the absolute requirement of all jobs. Use the right tool for the job. – minhee Sep 18 '11 at 17:25
  • 37
    _"Premature optimization is the root of all evil"_ or so Donald Knuth once said... – Yatharth Agarwal Oct 17 '12 at 13:07
  • 24
    Sticking with a well-known formatting scheme (as long as it suits the needs, which it does in the vast majority of cases), and which is twice as fast, is no "premature optimization" but simply reasonable. BTW, the `%` operator allows to reuse `printf` knowledge; dictionary interpolation is a very simple extension of the principle. – Tobias Jan 28 '13 at 23:03
  • 2
    From my test there is also a huge difference between Python3 and Python 2.7. Where `%` is much more efficient than `format()` in Python 3. The code that I used can be found here: https://github.com/rasbt/python_efficiency_tweaks/blob/master/test_code/string_subst_3.py and https://github.com/rasbt/python_efficiency_tweaks/blob/master/test_code/string_subst_2.py –  Jan 24 '14 at 14:15
  • 6
    I've actually experienced the opposite in one situation. New-style formatting was faster. Can you provide the test code you used? – David Sanders Oct 21 '14 at 19:54
  • 1
    >>> timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')") 0.7290658419951797 >>> timeit.timeit("'%d %f %s' % (1, 1.23, 'hello')") 0.01612339100393001 – Chris Cogdon Mar 30 '17 at 23:19
  • I still get similar results in Python 3.6.3. It is also not the attribute lookup which slows it down, `timeit.timeit("f(1, 1.23, 'hello')", setup="f = '{}{}{}'.format")` gives basically the same time. – Graipher Feb 23 '18 at 15:35
  • 'I guess Python is improving gradually.' Really? It looks like it got a lot slower across the board, not faster... – smaudet Jul 10 '19 at 12:13
  • Today, the new-new (1) `f-string literals` are now **far faster** than both (2) `% formatting`, and (3) `str.format`. Many of these comments are very old. – ninMonkey Aug 21 '19 at 10:22
  • I can confirm the results given in the answer: On my machine, f-strings are faster than `format()` but slower than `%`-formatting (at least judging from the above code). – balu Nov 24 '20 at 12:11
39

Yet another advantage of .format (which I don't see in the answers): it can take object properties.

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'

Or, as a keyword argument:

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

This is not possible with % as far as I can tell.

matiasg
  • 1,927
  • 2
  • 24
  • 37
  • 5
    This looks more unreadable than necessary compared to the equivalent `'x is {0}, y is {1}'.format(a.x, a.y)`. Should only be used when the `a.x` operation is very costly. – dtheodor Mar 29 '15 at 14:03
  • 13
    @dtheodor With a tweak to use a keyword argument instead of positional argument... `'x is {a.x}, y is {a.y}'.format(a=a)`. More readable than both examples. – CivFan Apr 17 '15 at 21:11
  • 1
    @CivFan Or, if you have more than one object, `'x is {a.x}, y is {a.y}'.format(**vars())` – Jacklynn Jun 18 '15 at 17:02
  • 1
    Also note this one in the same fashion: `'{foo[bar]}'.format(foo={'bar': 'baz'})`. – Antoine Pinsard Jul 23 '16 at 22:01
  • 5
    This is incredibly useful for customer-facing applications, where your application supplies a standard set of formatting options with a user-supplied format string. I use this all the time. The configuration file, for instance, will have some "messagestring" property, which the user can supply with `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}` or whatever they wish. This is far cleaner than trying to offer the same functionality with the old formatter. It makes user-supplied format strings way more powerful. – Taywee Sep 30 '16 at 21:33
33

As I discovered today, the old way of formatting strings via % doesn't support Decimal, Python's module for decimal fixed point and floating point arithmetic, out of the box.

Example (using 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))

Output:

0.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

There surely might be work-arounds but you still might consider using the format() method right away.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
balu
  • 3,500
  • 4
  • 34
  • 35
  • 1
    That's probably because new-style formatting calls `str(d)` before expanding the parameter, whereas old-style formatting probably calls `float(d)` first. – David Sanders Oct 21 '14 at 19:53
  • 4
    You'd think so, but `str(d)` returns `"3.12375239e-24"`, not `"0.00000000000000000000000312375239000000000000000000"` – Jacklynn Jun 18 '15 at 16:52
28

If your python >= 3.6, F-string formatted literal is your new friend.

It's more simple, clean, and better performance.

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)
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
zhengcao
  • 451
  • 5
  • 4
  • 1
    As of Python 3.11, C-style formatting (with %s, %a and %r) is [now as fast](https://docs.python.org/3.11/whatsnew/3.11.html#optimizations) as the corresponding f-string expression – TheFungusAmongUs Mar 20 '22 at 23:14
16

As a side note, you don't have to take a performance hit to use new style formatting with logging. You can pass any object to logging.debug, logging.info, etc. that implements the __str__ magic method. When the logging module has decided that it must emit your message object (whatever it is), it calls str(message_object) before doing so. So you could do something like this:

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

This is all described in the Python 3 documentation (https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles). However, it will work with Python 2.6 as well (https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages).

One of the advantages of using this technique, other than the fact that it's formatting-style agnostic, is that it allows for lazy values e.g. the function expensive_func above. This provides a more elegant alternative to the advice being given in the Python docs here: https://docs.python.org/2.6/library/logging.html#optimization.

David Sanders
  • 4,069
  • 1
  • 24
  • 38
  • 2
    I wish I could upvote this more. It allows logging with `format` without the performance hit -- does it by overriding `__str__` precisely as `logging` was designed for -- shortens the function call to a single letter (`N`) which feels very similar to some of the standard ways to define strings -- AND allows for lazy function calling. Thank you! +1 – CivFan Jan 26 '15 at 22:21
  • 2
    Is this any different in outcome to using the `logging.Formatter(style='{')` parameter? – davidA May 10 '17 at 02:03
12

One situation where % may help is when you are formatting regex expressions. For example,

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

raises IndexError. In this situation, you can use:

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

This avoids writing the regex as '{type_names} [a-z]{{2}}'. This can be useful when you have two regexes, where one is used alone without format, but the concatenation of both is formatted.

Jorge Leitao
  • 19,085
  • 19
  • 85
  • 121
  • 4
    Or just use `'{type_names} [a-z]{{2}}'.format(type_names='triangle|square')`. It's like saying `.format()` can help when using strings which already contain a percent character. Sure. You have to escape them then. – Alfe Mar 02 '17 at 16:24
  • 2
    @Alfe You are right, and that is why the answer starts with `"One situation where % may help is when you are formatting regex expressions."` Specifically, assume `a=r"[a-z]{2}"` is a regex chunk that you will be used in two different final expressions (e.g. `c1 = b + a` and `c2 = a`). Assume that `c1` needs to be `format`ed (e.g. `b` needs to be formatted runtime), but `c2` does not. Then you need `a=r"[a-z]{2}"` for `c2` and `a=r"[a-z]{{2}}"` for `c1.format(...)`. – Jorge Leitao Mar 02 '17 at 16:41
8

I would add that since version 3.6, we can use fstrings like the following

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

Which give

My name is john smith

Everything is converted to strings

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

Result:

mylist = ['foo', 'bar']

you can pass function, like in others formats method

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

Giving for example

Hello, here is the date : 16/04/2018

Sylvan LE DEUNFF
  • 682
  • 1
  • 6
  • 21
6

Python 3.6.7 comparative:

#!/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 __name__ == '__main__':
    main()

Output:

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
-----
Felix Martinez
  • 512
  • 5
  • 9
  • 4
    You should run each example several times, a single run may be misleading e.g. the operating system may be generally busy so execution of your code gets delayed. see the docs: https://docs.python.org/3/library/timeit.html. (nice avatar, Guybrush!) – jake77 Feb 08 '19 at 07:33
5

For python version >= 3.6 (see PEP 498)

s1='albha'
s2='beta'

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

#output
'albha      beta'
Parthian Shot
  • 1,390
  • 1
  • 13
  • 24
Roushan
  • 4,074
  • 3
  • 21
  • 38
3

But one thing is that also if you have nested curly-braces, won't work for format but % will work.

Example:

>>> '{{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}'
>>> 
U13-Forward
  • 69,221
  • 14
  • 89
  • 114