Embedding values directly inside string literals using f-strings eliminates manual concatenation. Prefix any string with f and wrap expressions in braces to inject values dynamically.
item = "banana"
count = 24
print(f"Inventory: {count} {item}")
Inline Debugging with Expressions
During troubleshooting, displaying both a variable's identifier and its value streamlines logging. Append an equals sign inside the braces to output the expression alongside its evaluation. Whitespace surrounding the operator is preserved in the output.
item = "banana"
count = 24
print(f"{item=}, {count = }")
Arithmetic inside Braces
Braces accept arbitrary expressions, not just variable names. Mathematical operations evaluate at runtime, and adding = reveals the computation and its result simultaneously.
score = 85
print(f"{score * 2 = }")
Object Representations
When inspecting strings, surrounding quotes often clarify boundaries. Rather than calling repr() externally, append !r within the braces to force a printable representation.
item = "banana"
print(f"{item!r}")
Numeric Precision
Format specifiers follow a colon inside the braces. To constrain floating-point output, apply width and precision controls such as .3f for three decimal places.
tax_rate = 0.0725
print(f"{tax_rate:.3f}")
Datetime Formatting
The same colon syntax formats datetime objects using standard strftime codes. Combine the = debugger with temporal specifiers to label timestamps.
from datetime import datetime
timestamp = datetime.utcnow()
print(f"{timestamp:%Y-%m-%d %H:%M}")
Alternative Formatting APIs
While f-strings embed expressions directly, the str.format() method accepts replacement fields as positional or keyword arguments, decoupling data from the template.
user = "Alice"
years = 28
print("{} is {} years old".format(user, years))
print("{name} is {age} years old".format(name=user, age=years))
Mastering Slice Assignment
Notation Essentials
Slice notation uses the form [start:stop:step]. The start index is inclusive, stop is exlcusive, and step defines the interval. Omitting values applies deafults: zero for start, sequence length for stop, and one for step.
values = [10, 20, 30, 40, 50]
values[1:4] # [20, 30, 40]
values[2:] # [30, 40, 50]
values[:3] # [10, 20, 30]
values[::2] # [10, 30, 50]
values[:] # [10, 20, 30, 40, 50]
Negative Indices and Reversal
Negative values count backward from the end. A negative step reverses traversal, effectively making the slice operate right-to-left.
values = [10, 20, 30, 40, 50]
values[-3:-1] # [30, 40]
values[::-1] # [50, 40, 30, 20, 10]
values[4:1:-1] # [50, 40, 30]
Boundary Tolerance
Slicing never raises index errors. Requests beyond the sequence boundaries simply return empty collections.
values = [10, 20, 30]
values[5:10] # []
values[:-8] # []
Mutating via Slice Assignment
Assigning to a slice replaces the selected segment with an iterable on the right-hand side. The target slice and replacement length need not match, allowing expansion or contraction of the original list.
values = [10, 20, 30, 40, 50]
values[:2] = [0] # [0, 30, 40, 50]
values[1:3] = [7, 8, 9] # [0, 7, 8, 9, 50]
values[-1:] = [60, 70] # [0, 7, 8, 9, 60, 70]
This technique provides a concise mechanism for bulk insertion, deletion, or replacement without explicit loops.