My take home is that every single version that is possible to do in old style formatting is longer in new style formatting. Even more so if the old-style formatting were written without the extraneous 1-ples.
'%4d' % 42
vs '{:4d}'.format(42)
It isn't surprising that so much code is still being written in old-style. The advantages of the new style, like padding with arbitrary characters; value reuse (particularly for I18n, though that tends to use a different mechanism for production systems that need it); and nested display; are really quite minor.This feeds into the other python issue on HN today - why does py3 have so little uptake? Because it is a solution to a problem few people have.
.format() is mostly too verbose for my tastes. Even Guido himself put it into the "meh" category in a talk. To be clear, I do use it when I need quick lightweight templating, say building a very complicated string.
But for most simple output printf style is shorter, simpler and I know it by heart. There was a guy at work who insisted on using .format() for everything that soured me on it, sometimes even simple string concatenation:
print 'result: {long_var_name}'.format({long_var_name=long_var_name})
Sometimes lines reached 200 chars. If lines were broken down, they might extend to 4 lines for each debug statement, adding tons of clutter. Said it was more readable because you could ignore the end. :/As sago said, printf is more concise in the majority of use cases. Not to mention the logging niceties:
log.info('result: %s %s', one, two) # shorter and note that fmt is deferred!
A _big_ one in my eyes. Too bad logging doesn't support {} internally.I've also wondered if .format could be improved if the % operator could be used with it, given appropriate precedence rules. Or perhaps use another char... dunno & or /?
print 'results: {} {}' % results
print 'results: {} {}' %% results
print 'results: {} {}' / results
Pycon, are you listening? ;) 'result: {long_var_name}'.format(long_var_name=long_var_name)
A similar option is possible with '%'-style formatting: 'result: %(long_var_name)s'%{"long_var_name":long_var_name}
-or-
'result: %(long_var_name)s'%dict(long_var_name=long_var_name)
The first of these alternatives is indeed shorter, so long as there's only a single parameter. If there are multiple parameters then the .format() solution will be shorter.My experience with '%(name)s' % dict formatting is that I would often forget the terminal 's', and write it as '%(name)'. For template-like strings, where I want named substitutions, I am very much in favor of {}-style formatting.
So if your co-worker insists on named substitutions, then s.format() is less verbose than s % dict.
Regarding logging, there's a section of how to use format templates in the logging system, at https://docs.python.org/3/howto/logging-cookbook.html#format... . In addition to using a LogRecord factory, you could reduce the overhead in old-style logging to only an extra constructor calls:
log.info(M('result: {} {}', one, two))
where M.__str__ forwards to str.format.Personally, I would like a way to pre-compile the format string:
fmt = format_compile("result: {} {} {}")
for result in results:
print(fmt(*result))
because currently (in Python 3.3) "%d %d %d" is about 40% faster than "{} {} {}".EDIT: Never mind: It can be faster to interpolate the string each time than to call a constructor:
% python3.3 -mtimeit 's="%s %s %s"' 's % (1, 3, 9)'
1000000 loops, best of 3: 0.36 usec per loop
% python3.3 -mtimeit 'class Spam:pass' 'Spam()'
10000 loops, best of 3: 23.6 usec per loop
I did not expect a factor of 65!I also import print_function from the future, mainly because you can break lines inside the parentheses.
I also use ' '.join([...]) for the same reason when building a large-ish string, you can break lines inside parentheses (or square brackets in this case).
I like breaking lines inside parentheses and other delimiters.
'%1$s %0$s' % ('one', 'two', )
Changing the padding character would probably not be too difficult either - just add the appropriate specifier to the formatting DSL; and I'm not so keen on supporting nested structures in the format string, as it seems to be going in the direction of allowing arbitrary expressions/Python code into a formatting DSL - one that I think shouldn't be more than a regular language.The various other improvements are all things I've made use of in real code. Format is now a tool that you can reach for before you need to go to a more heavyweight templating lib (like Jinja2).
'{p.type}: {p.kinds[0][name]}'.format(p=Plant())'{}'.format(format_decimal_with_prepended_spaces(42, 4))
With this approach, my own app needs to do the actually formatting, while `format()` merely concats.
a = 4
'A is: {a}'.format(**locals())
works as expected. a = 4
'A is: %(a)d' % locals()
Perhaps is my experience programming in C, but "format" looks lees familiar and I tend to use the old style formatting. def foo():
a = 4
def bar():
print("a = {a}".format(**locals()))
bar()
foo()
which will raise a KeyError, while it will work fine if you add a print(a)
to the end of bar(). def foo():
a = 4
def bar():
a
print("a = {a}".format(**locals()))
bar()
foo()
successfully prints a = 4
which is a bit confusing, but makes sense if you think about it. a = 4
'A is:{}'.format(a) a = 3
b = 4
c = 6
"A is {a}, C is {c}, B is {b}".format(**locals())
Then you don't have to figure out specifying order or definitions like ".format(a=a, b=b, c=c)". I'd probably go the more explicit route in production code anyway, but for debugging / unstable development it seems useful.• it is supposedly significantly faster
• new, means, eventually we'll have to use it, so might as well get used to it now!
• it's a method on an object, which makes my brain happy. I never managed to get my brain wrapped around the old style syntax, it's inconsistent with the rest of the python syntax.
% python3.3 -mtimeit 's="%s %s %s"' 's % (1, 3, 9)'
1000000 loops, best of 3: 0.361 usec per loop
% python3.3 -mtimeit 's="{} {} {}"' 's.format(1, 3, 9)'
1000000 loops, best of 3: 0.593 usec per loop
% python3.3 -mtimeit 'f="{} {} {}".format' 'f(1, 3, 9)'
1000000 loops, best of 3: 0.569 usec per loop
The construct "x % y" has special support in CPython. Quoting from ceval.c: TARGET(BINARY_MODULO)
w = POP();
v = TOP();
if (PyUnicode_CheckExact(v))
x = PyUnicode_Format(v, w);
else
x = PyNumber_Remainder(v, w);
Py_DECREF(v);
Py_DECREF(w);
SET_TOP(x);
if (x != NULL) DISPATCH();
break;
That is, in the '%' case, if the left-hand side is a string, then go directly to the string format. Otherwise, go though the normal binary operation resolution mechanism.While the "".format(x) path goes through the normal method invocation path - it has no special treatment.
uhh... so you'd prefer to do something like `1.add(2)` ???
However, the fact that Python 3 just went off and did it's own things rather than the usual cycle of:
1) improve a language feature
2) deprecate the old way of doing it
3) give people time to update code (usually a couple of point releases)
4) remove features that have been replaced
Python 3 should not have dropped lots of little changes with no backwards compatibility. They should still make 2.8 and 2.9 that are releases that remove features and add new ones until most python code works in Python 3. if is_about_python3(post):
insert_token_random_complaint_from_2008()
(edit: snake-case)improve a language feature & deprecate the old way of doing it -> Python 2.6+ and 3.2+
remove features that have been replaced -> 3.x
Did I also mention it is not hard to have a single code base that is compatible with both 2.x and 3.x?
Since Centos 5 still has about 2 years of life but fewer and fewer Python packages support 2.4, it might be worth looking at Pyenv:
Good overview of what it lets you do:
http://fgimian.github.io/blog/2014/04/20/better-python-versi...
Python 2.4 is actually pretty ancient. To put it into perspective, it's from when we had things Firefox 2.0, and IE6 was the latest IE.
Why not 'field: {:center :minsize=8 varname}'.format(varname=123) ?
'{:center :minsize=8 varname} {:center :minsize=8 varname2} {:center :minsize=8 varname2}'.format(varname=123, varname2=123,varname3=123)
instead of '{:^8} {:^8} {:^8}'.format(varname, varname2, varname3)
Concision is not always a good thing, but still...also, if you are going to repeat always the same, you can standardize it in a string var and just use that instead of repeating.
and if you are NOT, then it beats '{:^8} {:*8} {:@8} {:☃8}' anytime, because now you have to know and notice the change in those cryptic chars.
'%d %d' % (1, 2)
I would have done this as '%s %s' % (1, 2)
Because we're turning everything into a string at the end of the day anyway!Yeah. Having finished the article, .format() just isn't really needed. If I'm at the point where I'm doing templating with key:values, I'll be using jinja.
>>> '%d' % 3.14
'3'
>>> '%d' % 'foo'
TypeError: %d format: a number is required, not str logging.debug('some value: %d', 123)
which avoids the cost of string interpolation if that log message wasn't going to be emitted, say because the log level is higher than DEBUG. If you instead write: logging.debug('some value: {}'.format(123))
then Pylint will complain that I "Use % formatting in logging functions but pass the % parameters as arguments (logging-format-interpolation)".Yes, I can disable that particular warning, either by maintain a pylintrc file or adding annotations to the source code. Yes, this is a dumb complaint. But yes, it still bugs me.
-- The Zen of Python
>>> "{:>40,.2f}".format(sys.maxint)
' 9,223,372,036,854,775,808.00' Basic formatting
Old: '%d %d' % (1, 2)
New: '{} {}'.format(1, 2)
It should rather be `{:d} {:d}'.format(1, 2)` but even that isn't strictly equivalent (try both styles with a float or a Decimal).basically locals(), vars(), and globals() I find very useful for string formatting.
Its good, but I think this is even better.