Strings and Text
|
This section documents the current Python release line as published at the official Python documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
A Python str is an immutable sequence of Unicode code points — every "modification" method returns
a new string, and text is always kept distinct from raw bytes. This page covers literals, the
methods you reach for daily, the three ways to format text, and the str/bytes split that underlies
all of it.
String Literals, Immutability, Indexing and Slicing
Single and double quotes are interchangeable single-line literals; triple-quoted strings ('''…''' or
"""…""") span multiple lines and are how docstrings are written. A r prefix makes a raw string,
where backslashes are kept literally — essential for regular expressions and Windows paths — while a
plain string interprets escape sequences such as \n, \t, and \\. Details are in the tutorial’s
Strings section and the full escape-sequence
table in the lexical analysis reference’s
String and Bytes
literals:
single = 'hello'
double = "hello"
triple = """line one
line two"""
path = r"C:\Users\name\docs" # raw: backslashes are literal
escaped = "C:\\Users\\name\\docs" # same characters, escaped manually
print(path == escaped) # True
A str is a sequence type — see
Text Sequence Type — str — so indexing and slicing work exactly like on a list, except the result of any slice is itself a
str. Because strings are immutable, indexed or sliced assignment is not possible; build a new string
instead:
word = "Python"
print(word[0]) # 'P'
print(word[-1]) # 'n' (negative index counts from the end)
print(word[2:5]) # 'tho'
print(word[:3]) # 'Pyt'
print(word[::-1]) # 'nohtyP' (reversed, via a step of -1)
# word[0] = "J" # TypeError: 'str' object does not support item assignment
new_word = "J" + word[1:]
print(new_word) # 'Jython'
Common String Methods
Every method below returns a new string (or list); the original is never changed in place. The full catalogue is under String Methods:
raw = " Hello, World! "
print(raw.strip()) # 'Hello, World!' (both ends)
print(raw.strip().lower()) # 'hello, world!'
print(raw.strip().upper()) # 'HELLO, WORLD!'
csv_line = "alice,30,engineer"
fields = csv_line.split(",") # ['alice', '30', 'engineer']
print(fields)
print("-".join(fields)) # 'alice-30-engineer'
print(raw.strip().replace("World", "Python")) # 'Hello, Python!'
print("report.CSV".endswith(".csv")) # False: case-sensitive
print("report.CSV".lower().endswith(".csv")) # True
print("report.csv".startswith("report")) # True
Formatting: f-strings, .format(), and %
f-strings (the modern default)
A formatted string literal — an f prefix — evaluates any expression inside \{ \} and inserts its
result. This is the preferred way to format text in modern Python; the grammar lives in the reference’s
Formatted string literals, and the
mini-language after a : (widths, alignment, precision, thousands separators, and so on) is the same
Format Specification
Mini-Language used by .format():
name = "Ada"
age = 36
print(f"Hello, {name}! You are {age} years old.") # Hello, Ada! You are 36 years old.
print(f"{age * 2}") # 72 -- any expression is allowed
pi = 3.14159265
print(f"{pi:.2f}") # '3.14' -- fixed-point, 2 decimals
print(f"{1_000_000:,}") # '1,000,000' -- thousands separator
print(f"{name:>10}") # ' Ada' -- right-aligned in a width-10 field
Since Python 3.8, adding = after an expression prints both the source text and its value — a quick
way to inline debug prints without writing the variable name twice:
count = 7
print(f"{count=}") # count=7
print(f"{count * 2=}") # count * 2=14
.format()
str.format() predates f-strings and is still common in code that assembles a template string
separately from the values (translated strings, logging templates). Positional and keyword placeholders
work the same way, documented at
Format String Syntax:
template = "{0} scored {1} points"
print(template.format("Ada", 95)) # Ada scored 95 points
template2 = "{name} scored {score} points"
print(template2.format(name="Ada", score=95)) # Ada scored 95 points
data = {"name": "Ada", "score": 95}
print(template2.format(**data)) # Ada scored 95 points -- unpack a dict as keywords
%-formatting (legacy — read, don’t write)
The oldest style, borrowed from C’s printf, is documented at
printf-style String
Formatting. It still turns up in older codebases and logging calls, so it is worth recognizing, but new
code should prefer f-strings:
name = "Ada"
score = 95
print("%s scored %d points" % (name, score)) # Ada scored 95 points
str vs. bytes: Unicode Fundamentals
Python 3 draws a hard line between text (str, a sequence of Unicode code points) and binary data
(bytes, a sequence of integers 0-255) — there is no implicit conversion between them, unlike Python
2’s blurred str/unicode split. The design rationale and history are in the
Unicode HOWTO. Converting between the two is always
explicit: .encode() turns str into bytes using a named encoding (UTF-8 by default and the
overwhelming default in practice), and .decode() reverses it:
text = "café" # str: Unicode code points
encoded = text.encode("utf-8") # bytes: b'caf\xc3\xa9'
print(encoded)
print(type(encoded)) # <class 'bytes'>
decoded = encoded.decode("utf-8") # back to str
print(decoded) # café
print(decoded == text) # True
# text + encoded # TypeError: can only concatenate str (not "bytes") to str
A bytes literal uses a b prefix and can only contain ASCII bytes; a mismatched encoding when
decoding raises rather than silently corrupting data:
>>> b"hello"
b'hello'
>>> "café".encode("ascii")
Traceback (most recent call last):
...
UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 3: ordinal not in range(128)
>>> "café".encode("utf-8").decode("ascii")
Traceback (most recent call last):
...
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)
Keeping str and bytes separate types — rather than one type that is "text until it isn’t" — is
exactly what unified Python 3’s text handling: a function that expects text can never be silently handed
undecoded bytes, and vice versa.
See Also
-
Collections — lists, tuples, dicts, and sets that strings are often split into or joined from.
-
Standard Library Tour — where
re,textwrap, and other text-processing modules live.