Strings are one of Python's most versatile data types. They represent sequences of Unicode characters and come with a rich set of methods for manipulation, formatting, and analysis. This guide covers everything from basic creation to advanced formatting and encoding.
String Creation
Python provides several ways to create strings. Single and double quotes are interchangeable; triple quotes enable multi-line strings.
Use single quotes by default unless the string contains single quotes. Use triple quotes for docstrings and multi-line text. Python has no char type — a single character is just a string of length 1.
Escape Sequences & Raw Strings
Escape sequences let you embed special characters. Raw strings (r"...") disable escaping and are essential for regex patterns and Windows paths.
Always prefer f-strings for string formatting in modern Python (3.6+). They are faster, more readable, and less error-prone than str.format() or %-formatting. Use !r for debug output and .2f for fixed-precision floats.
str.format() & %-Formatting
Before f-strings, Python offered two older formatting approaches. You may encounter them in legacy codebases.
formatting_legacy.py
Python
1
# === str.format() (Python 2.6+) ===
2
3
# Positional arguments
4
print("{} is {} years old".format("Alice",30))
5
# "Alice is 30 years old"
6
7
# Indexed positional
8
print("{1} is {0} years old".format(30, "Alice"))
9
# "Alice is 30 years old"
10
11
# Named placeholders
12
print("{name} is {age} years old".format(name="Bob", age=25))
print("\u00B2".isdecimal()) # False (superscript not base-10)
✓
best practice
Chain methods carefully: " Hello ".strip().lower() works, but chaining too many methods can hurt readability. Use intermediate variables for complex pipelines.
String Slicing
Slicing extracts substrings using the [start:stop:step] syntax. All indices are zero-based, and stop is exclusive. Negative indices count from the end.
Slice
Result
Explanation
s[0:5]
"Hello"
Characters 0 through 4
s[:5]
"Hello"
Start defaults to 0
s[6:]
"World!"
Stop defaults to end
s[::2]
"HloWrd"
Every second character
s[::-1]
"!dlroW olleH"
Reversed string
s[-5:]
"orld!"
Last 5 characters
s[-3:-1]
"ld"
Negative indices
slicing.py
Python
1
s = "Hello, World!"
2
3
# Basic slicing
4
print(s[0:5]) # "Hello"
5
print(s[7:12]) # "World"
6
print(s[:5]) # "Hello" (start = 0)
7
print(s[7:]) # "World!" (stop = end)
8
print(s[:]) # "Hello, World!" (full copy)
9
10
# Negative indices
11
print(s[-6:-1]) # "World" (from -6 up to -1)
12
print(s[-5:]) # "orld!" (last 5 chars)
13
14
# Step
15
print(s[::2]) # "HloWrd" (every 2nd char)
16
print(s[1::2]) # "el,ol!" (every 2nd, starting at 1)
17
print(s[::-1]) # "!dlroW ,olleH" (reverse)
18
19
# Combined
20
print(s[2:9:3]) # "l,W" (indices 2,5,8)
21
print(s[7:2:-1]) # "W ,oll" (reverse slice)
22
23
# Slicing is safe — out-of-range handled gracefully
24
print(s[0:100]) # "Hello, World!" (no error)
25
print(s[100:]) # "" (empty string)
ℹ
info
Slicing never raises IndexError — out-of-range indices are silently clamped. This makes slicing safe for string processing. Use s[::-1] for the idiomatic Python reverse.
String Immutability
Strings are immutable in Python — once created, they cannot be changed in-place. Every operation that modifies a string returns a new string object.
immutability.py
Python
1
s = "hello"
2
3
# This raises TypeError:
4
# s[0] = "H" # TypeError: 'str' object does not support item assignment
5
6
# Instead, create a new string:
7
s = "H" + s[1:] # "Hello"
8
9
# Every string method returns a new string
10
original = " Hello "
11
stripped = original.strip()# original is unchanged
12
print(original)# " Hello " (still has spaces)
13
print(stripped)# "Hello"
14
15
# Concatenation creates new strings (potentially expensive)
16
s = ""
17
for word in ["a", "b", "c"]:
18
s = s + word # new string each iteration — O(n^2)
19
20
# Identity check
21
a = "hello"
22
b = "hello"
23
print(a is b) # True (CPython interning for small strings)
24
print(id(a) == id(b)) # True (same object)
25
26
# But not all strings are interned
27
c = "hello world!"
28
d = "hello world!"
29
print(c is d) # False (CPython may not intern long strings)
30
31
# Always use == for string comparison, not 'is'
32
print(c == d) # True (correct)
Unicode & Encoding
Python 3 strings are sequences of Unicode code points. The encode() method converts a string to bytes, while decode() converts bytes back to a string.
Always use encode() with an explicit encoding (usually "utf-8"). Never rely on the system default encoding. For string comparison involving accented characters, normalize first with unicodedata.normalize().
Best Practices
Concatenation: join() vs +
Using + in a loop creates a new string each iteration, leading to O(n²) time. The str.join() method pre-allocates and is always the right choice for joining multiple strings.
concat_best_practices.py
Python
1
# BAD: O(n^2) string concatenation in a loop
2
result = ""
3
for word in ["this", "is", "slow"]:
4
result += word + " "
5
6
# GOOD: O(n) join
7
result = " ".join(["this", "is", "fast"])
8
9
# Even better: generator expression
10
result = " ".join(word for word in["a", "b", "c"])
11
12
# Pre-allocate list then join
13
parts = []
14
for i in range(100):
15
parts.append(str(i))
16
result = ", ".join(parts)
17
18
# Small number of strings: + is fine
19
full_name = first_name + " " + last_name
The string Module
Python's string module provides useful constants and utilities.