Videos
Hello i am a very very beginner coder. Iโm in a beginner python class and I feel like the text books just throws stuff at concepts. Why are fโstrings important? If Iโm understanding it right I feel like the same output could get accomplished in a different way.
I was trying to make a, silly program in which you can do some random things. I used arguments (%)in the past, but it's a bit hard to write like the identification of the variable, like there is so much types (%s, %f, %2f, %d, %g, %e...) and I want to take the easiest to write or the easiest to debug.
here are some examples of using each one:
using (%):
name = "Imaginary_Morning"
age = 13
print("This profile is called %s and it has %d years old." % (name, age))
using (f"string"):
name = "Imaginary_Morning"
age = 13
print(f"This profile is called {name} and it has {age} years old.")
using (.format()):
name = "Imaginary_Morning"
age = 13
print("This profile is called {} and it has {} years old.".format(name, age))you should to put the format_string as variable
temp = f'{i:{format_string}}' + temp
the next code after : is not parsed as variable until you clearly indicate.
And thank @timpietzcker for the link to the docs: formatted-string-literals
You need to keep the alignment and padding tokens separate from each other:
def display_pattern(n):
padding = 4
align = ">"
temp = ''
for i in range(1, n + 1):
temp = f'{i:{align}{padding}}' + temp
print(temp)
EDIT:
I think this isn't quite correct. I've done some testing and the following works as well:
def display_pattern(n):
align = ">4"
temp = ''
for i in range(1, n + 1):
temp = f'{i:{align}}' + temp
print(temp)
So I can't really say why your method wouldn't work...
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().
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
I'm afraid that it will be deprecated during the next Python versions
Don't be, str.format does not appear (nor has a reason) to be leaving any time soon, the PEP that introduced fprefixed-strings even states in its Abstract:
This PEP does not propose to remove or deprecate any of the existing string formatting mechanisms.
Formatted strings were introduced to address some of the shortcomings other methods for formatting strings had; not to throw the old methods away and force god-knows how many projects to use f-string's if they want their code to work for Python 3.6+.
As for the performance of these, it seems my initial suspicion that they might be slower is wrong, f-strings seem to easily outperform their .format counterparts:
โ cpython git:(master) ./python -m timeit -s "a = 'test'" "f'formatting a string {a}'"
500000 loops, best of 5: 628 nsec per loop
โ cpython git:(master) ./python -m timeit "'formatting a string {a}'.format(a='test')"
100000 loops, best of 5: 2.03 usec per loop
These were done against the master branch of the CPython repository as of this writing; they are definitely subject to change:
f-strings, as a new feature, might have possible optimizations- Optimizations to CPython might make
.formatfaster (e.g Speedup method calls 1.2x)
But really, don't worry about speed so much, worry about what is more readable to you and to others.
In many cases, that's going to be f-strings, but there's some cases where format is better.
To build on Jim's answer and address your performance concern, I used python's dis module to compare the bytecode instructions for two syntactically different, but functionally equivalent functions.
import dis
def f1():
a = "test"
return f"{a}"
def f2():
return "{a}".format(a='test')
print(dis.dis(f1))
print(dis.dis(f2))
The result of which is:
11 0 LOAD_CONST 1 ('test')
2 STORE_FAST 0 (a)
12 4 LOAD_FAST 0 (a)
6 FORMAT_VALUE 0
8 RETURN_VALUE
None
15 0 LOAD_CONST 1 ('{a}')
2 LOAD_ATTR 0 (format)
4 LOAD_CONST 2 ('test')
6 LOAD_CONST 3 (('a',))
8 CALL_FUNCTION_KW 1
10 RETURN_VALUE
None
One can see that the f-string handles the formatting without attribute or function calls, which can impose type checking and memory overhead. According to timeit this results in a roughly 3x performance gain (for my specific functions)
>>> timeit.timeit('f1()', 'from __main__ import f1', number=100000)
0.012325852433775708
>>> timeit.timeit('f2()', 'from __main__ import f2', number=100000)
0.036395029920726074