Skip to content

Commit 232c895

Browse files
BNAndrasBNAndrasBethanyG
authored
Tweaks to string-formatting concept (#4099)
* Tweaks to `string-formatting` concept * cannot to can not * Add self to contributors * Add t-strings link * Add examples * can to could Co-authored-by: BethanyG <BethanyG@users.noreply.github.com> * considerations to use-cases Co-authored-by: BethanyG <BethanyG@users.noreply.github.com> * A f-string to an f-string Co-authored-by: BethanyG <BethanyG@users.noreply.github.com> --------- Co-authored-by: BNAndras <bnandras@example.com> Co-authored-by: BethanyG <BethanyG@users.noreply.github.com>
1 parent a3a82a3 commit 232c895

3 files changed

Lines changed: 121 additions & 46 deletions

File tree

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{
22
"blurb": "There are four main string formatting methods. A '%' formatting mini-language is supported, but is considered outdated. String interpolation (f-strings) and 'str.format()'are newer, and can be used for complex or conditional substitution. 'string.template()' substitution is used for internationalization, where f-strings will not translate.",
3-
"authors": ["valentin-p"],
4-
"contributors": ["j08k", "BethanyG"]
3+
"authors": [
4+
"valentin-p"
5+
],
6+
"contributors": [
7+
"j08k",
8+
"BethanyG",
9+
"BNAndras"
10+
]
511
}

concepts/string-formatting/about.md

Lines changed: 101 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,57 +30,94 @@ In this example, we insert two variable values in the sentence: one `str` and on
3030
The expressions evaluated can be almost anything.
3131
Some of the (wide range) of possibilities that can be evaluated: `str`, `numbers`, variables, arithmetic expressions, conditional expressions, built-in types, slices, functions, lambdas, comprehensions or **any** objects with either `__str__` or `__repr__` methods defined.
3232

33-
Some examples:
33+
Going from simple to complex:
34+
35+
**Inserting a variable** — the simplest use of a f-string is to place a variable directly into the string.
36+
37+
```python
38+
# Assigning a variable
39+
>>> name = "World"
40+
41+
# Inserting that variable
42+
>>> f'Hello, {name}!'
43+
'Hello, World!'
44+
```
45+
46+
**Expressions inside `{}`** — any valid Python expression can be evaluated inside the braces.
47+
Note that using double quotes inside a single-quoted f-string (or vice versa) avoids the need for escape sequences:
3448

3549
```python
36-
# A dictionary of key:value pairs.
50+
# A dictionary of key:value pairs
3751
>>> waves = {'water': 1, 'light': 3, 'sound': 5}
3852

39-
# Using the name waves in an f-string.
40-
>>> f'"A dict can be represented with f-string: {waves}."'
41-
'"A dict can be represented with f-string: {\'water\': 1, \'light\': 3, \'sound\': 5}."'
53+
# Inserting the whole dict
54+
>>> f'Wave ranks: {waves}'
55+
"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"
56+
57+
# An expression can be evaluated inline
58+
>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
59+
"Tenfold the value of 'light' is 30."
60+
61+
# A method call can also be evaluated inline
62+
>>> f'{"hello world!".title()} is a classic greeting.'
63+
'Hello World! is a classic greeting.'
4264

43-
# Here, we pull a value from the dictionary by using the key
44-
>>> f'Tenfold the value of "light" is {waves["light"] * 10}.'
45-
'Tenfold the value of "light" is 30.'
65+
# An f-string can be nested inside another f-string
66+
>>> f"{f'hello world!'.title()} is a classic greeting."
67+
'Hello World! is a classic greeting.'
4668
```
4769

48-
Replacement fields (_the `{}` in the f-string_) support output control mechanisms such as width, alignment, precision.
49-
This specification is started in the [format specification mini-language][format-mini-language].
70+
**Output formatting** — the [format specification mini-language][format-mini-language] can be used to control alignment, numeric precision, and much more.
71+
The format specification goes after the value, separated by a `:`.
5072

51-
A more complex example of an `f-string` that includes output control:
73+
```python
74+
# Right-align a value to ten characters, rounding it to 3 decimal places.
75+
>>> value = 1 / 7
76+
>>> f'One seventh is {value:10.3f}.'
77+
'One seventh is 0.143.'
78+
79+
# A format specification can be set using variables as well.
80+
>>> padding = 10
81+
>>> precision = 3
82+
>>> f'One seventh is {value:{padding}.{precision}f}.'
83+
'One seventh is 0.143.'
84+
```
85+
86+
**Putting it all together** — variables, expressions, function calls, and output formatting:
5287

5388
```python
54-
# Assigning variables
5589
>>> precision = 3
56-
>>> verb = "see"
57-
>>> the_end = ['end', 'of', 'transmission']
90+
>>> f"{30e8 * 111_000:6.{precision}e}"
91+
'3.330e+14'
5892

59-
# Reassigning verb to 'meet'.
6093
>>> verb = 'meet'
94+
>>> the_end = ['end', 'of', 'transmission']
95+
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
96+
'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'
6197

62-
# This example includes a function, str, a nested f-string, an arithmetic expression,
63-
# precision formatting, bracket escaping and object formatting.
64-
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {f"{30e8 * 111_000:6.{precision}e}"} light-years."{{{the_end}}}'
65-
'"Have a nice day, I will meet you after 3.330e+14 light-years."{[\'end\', \'of\', \'transmission\']}'
98+
# Did you notice the escaped single-quotes in the previous example?
99+
# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
100+
>>> f"Have a nice day. {the_end}"
101+
"Have a nice day. ['end', 'of', 'transmission']"
66102
```
67103

68-
There are a few limitations to be aware of.
69-
`f-string` expressions cannot be empty, they cannot contain comments.
104+
There are two main limitations to be aware of.
105+
`f-string` expressions can not be empty.
106+
[Additionally, before Python 3.12, they could not contain comments.][pep-0701]
70107

71108
```python
72109
>>> f"An empty expression will error: {}"
73110
SyntaxError: f-string: empty expression not allowed
74111

75112
>>> word = 'word'
76-
>>> f"""A comment in a triple quoted f-string will error: {
113+
>>> f"""A comment in a triple quoted f-string: {
77114
word # I chose a nice variable
78115
}"""
79-
SyntaxError: f-string expression part cannot include '#'
116+
'A comment in a triple quoted f-string: word'
80117
```
81118

82119
~~~~exercism/caution
83-
String interpolation cannot be used together with the [GNU gettext API][gnu-gettext-api] for internationalization (I18N) and localization (L10N), so it is recommended that the `string.Template(template)` class or the `str.format()` method outlined below be used instead of an `f-string` in any "string wrapping" translation scenarios.
120+
String interpolation can not be used together with the [GNU gettext API][gnu-gettext-api] for internationalization (I18N) and localization (L10N), so it is recommended that the `string.Template(template)` class or the `str.format()` method outlined below be used instead of an `f-string` in any "string wrapping" translation scenarios.
84121
85122
Also keep in mind that using expressions inside the `f-string` brackets `{}` is similar to using `eval()` or `exec()`, so it isn't very safe and should be used sparingly.
86123
~~~~
@@ -105,7 +142,7 @@ The complete formatting specifier pattern is `{[<name>][!<conversion>][:<format_
105142
- `<name>` can be a named placeholder or a number or empty.
106143
- `!<conversion>` is optional and should be one of this three conversions: `!s` for [`str()`][str-conversion], `!r` for [`repr()`][repr-conversion] or `!a` for [`ascii()`][ascii-conversion].
107144
By default, `str()` is used.
108-
- `:<format_specifier>` is optional and has a lot of options, which we are [listed here][format-specifiers].
145+
- `:<format_specifier>` is optional and controls how the value is displayed. More information about possible options can be [found here][format-specifiers].
109146

110147
Example of conversions for a diacritical letter:
111148

@@ -132,13 +169,39 @@ Example of conversions for a diacritical letter:
132169
"She said her name is not Chloe but 'Zoë'."
133170
```
134171

135-
Example of using format specifiers:
172+
Examples of common format specifiers:
136173

137174
```python
138-
# Formats the object at index 0 as a decimal with zero places,
139-
# then as a right-aligned binary number in an 8 character wide field.
140-
>>> "The number {0:d} has a representation in binary: '{0: >8b}'.".format(42)
141-
"The number 42 has a representation in binary: ' 101010'."
175+
# Integer and binary/hex representations of the same number
176+
>>> my_num = 42
177+
>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
178+
"42 in binary is 101010. In hex, it is 2a"
179+
180+
# Alignment: left (<), right (>), and center (^) using up to ten characters total
181+
>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
182+
"[left ] [ right] [ center ]"
183+
184+
# Float precision and scientific notation up to three decimal places
185+
>>> pi = 3.141592653589793
186+
>>> f"fixed: {pi:.3} scientific: {pi:.3e}"
187+
"fixed: 3.142 scientific: 3.142e+00"
188+
189+
# Thousands separator and percentage
190+
>>> balance = 1000
191+
>>> rate = 0.0225
192+
>>> f"Balance: ${balance:,.0f} Interest rate: {rate:.1%}"
193+
"Balance: $1,000 Interest rate: 2.2%"
194+
195+
# Putting it all together
196+
>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
197+
>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
198+
>>> print(header)
199+
Item Qty Price
200+
>>> for name, qty, price in items:
201+
... print(f"{name:<12} {qty:>6} {price:>9.2f}")
202+
Widget 1250 9.99
203+
Gadget 37 24.50
204+
Doohickey 4 149.00
142205
```
143206

144207
More examples are shown at the end of [this documentation][summary-string-format].
@@ -177,8 +240,10 @@ If you want to add multiple variables to a string, you need to supply a [tuple][
177240

178241
## Template Strings
179242

180-
[`string.Template()`][string.Template()] is a class from the `string` module (_as opposed to the built-in `str` type_), which is part of the Python standard library, but has to be imported for use.
181-
Template strings support `$`-based substitution and are much simpler and less capable than the other options mentioned here, but can be very useful for when complicated internationalization is needed, or outside inputs need to be sanitized.
243+
[`string.Template()`][string.Template()] (_not to be confused with Python 3.14 [t-strings]_) is a class from the `string` module (_as opposed to the built-in `str` type_), which is part of the Python standard library, but has to be imported for use.
244+
Template strings support `$`-based substitution and are much simpler and less capable than the other options mentioned here.
245+
However, they can be very useful for when complicated internationalization is needed, or outside inputs need to be sanitized.
246+
`string.Template` is considered safer for untrusted user input because it prevents evaluating arbitrary expressions or accessing object attributes, which mitigates format-string injection attacks.
182247

183248
```python
184249
>>> from string import Template
@@ -204,8 +269,8 @@ A few quick guidelines:
204269
If you don't need to internationalize, they should be the Python 3.6+ preferred method.
205270
2. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
206271
3. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs from users need to be handled, and for wrapping translation strings.
207-
4. The `%` operator is not supported in some newer distributions of Python and should mostly be used for compatibility with old code.
208-
`%` formatting` can lead to issues displaying non-ascii and unicode characters and has more errors and less functionality than other methods.
272+
4. The `%` operator is generally considered deprecated for new code, though it still works in modern Python. It should mostly be used for compatibility with older codebases.
273+
`%` formatting can lead to issues displaying non-ASCII and Unicode characters and has more errors and less functionality than other methods. Check your specific Python distribution for support details if you intend to use it.
209274

210275
If you want to go further: [all about formatting][all-about-formatting] and [Python String Formatting Best Practices][formatting best practices] are good places to start.
211276

@@ -216,6 +281,7 @@ If you want to go further: [all about formatting][all-about-formatting] and [Pyt
216281
[format-specifiers]: https://www.python.org/dev/peps/pep-3101/#standard-format-specifiers
217282
[formatting best practices]: https://realpython.com/python-string-formatting/
218283
[pep-0498]: https://peps.python.org/pep-0498
284+
[pep-0701]: https://peps.python.org/pep-0701/
219285
[printf-style-docs]: https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
220286
[repr-conversion]: https://www.w3resource.com/python/built-in-function/repr.php
221287
[str-conversion]: https://www.w3resource.com/python/built-in-function/str.php
@@ -224,5 +290,6 @@ If you want to go further: [all about formatting][all-about-formatting] and [Pyt
224290
[string.Template()]: https://docs.python.org/3/library/string.html#template-strings
225291
[summary-string-format]: https://www.w3schools.com/python/ref_string_format.asp
226292
[template-string]: https://docs.python.org/3/library/string.html#template-strings
293+
[t-strings]: https://realpython.com/python-t-strings/
227294
[tuples]: https://www.w3schools.com/python/python_tuples.asp
228295
[zen-of-python]: https://www.python.org/dev/peps/pep-0020/

concepts/string-formatting/introduction.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
## String Formatting in Python
44

55
The [Zen of Python][zen-of-python] asserts there should be "one _obvious_ way to do something in Python".
6-
But when it comes to string formatting, things are a little .... _less zen_.
7-
It can be surprising to find out that there are **four** main ways to perform string formatting in Python - each for a different scenario.
8-
Some of this is due to Python's long history and some of it is due to considerations like internationalization or input sanitation.
6+
For Python 3.6+, **literal string interpolation** (**`f-strings`**) is often the obvious and preferred way to format strings:
97

10-
With 4 different paths to take, how do you decide what to use?
8+
```python
9+
>>> adjective = "easy"
10+
>>> f"This is an {adjective} way to format strings!"
11+
'This is an easy way to format strings!'
12+
```
1113

12-
1. `f-strings` are the newest and easiest to read.
13-
If you don't need to internationalize, they should be the Python 3.6+ preferred method.
14-
2. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
15-
3. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs need to be handled and for wrapping translation strings.
16-
4. The `%` operator should mostly be used for compatibility with old code.
17-
`%` formatting` can lead to issues displaying non-ascii and unicode characters and has more errors and less functionality than other methods.
14+
However, given Python's long history and different use-cases, it might not be surprising that there are **three** other common ways to perform string formatting in Python:
15+
16+
1. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
17+
2. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs need to be handled and for wrapping translation strings.
18+
3. The `%` operator is generally considered deprecated for new code, though it still works in modern Python.
19+
It should mostly be used for compatibility with older codebases. `%` formatting can lead to issues displaying non-ASCII and Unicode characters and has more errors and less functionality than other methods.Check your specific Python distribution for support details if you intend to use it.
1820

1921
If you want to go further: [all about formatting][all-about-formatting] and [Python String Formatting Best Practices][formatting best practices] are good places to start.
2022

0 commit comments

Comments
 (0)