1413 words
7 minutes
A Practical Guide to Python Strings: Quotes, Prefixes, and Formatting

Python strings look simple, but their quotes, prefixes, and formatting rules can be confusing at first. A useful way to understand them is to ask three questions:

  1. Which quotes define the string?
  2. What do prefixes such as f, r, b, and u mean?
  3. How do you insert and format values inside a string?

This guide answers each question and explains the edge cases worth knowing.

1. Single and Double Quotes#

Single and double quotes both create values of type str. They behave exactly the same:

name1 = 'Bob'
name2 = "Bob"
print(name1 == name2) # True

Choose the style that makes the text easiest to read and requires the fewest escapes:

text1 = "I'm Bob"
text2 = 'She said, "Hello."'

If the content contains the same quote used to define the string, escape it with a backslash:

text = 'I\'m Bob'
print(text) # I'm Bob

Most projects adopt one quote style for consistency and switch only when it improves readability.

2. Triple Quotes and Multiline Strings#

Three single quotes (''') or three double quotes (""") create a string that can span several lines:

text = """First line
Second line
Third line"""
print(text)

Triple-quoted strings are useful for:

  • Multiline text
  • SQL or HTML fragments
  • Module, class, function, and method docstrings
def add(a, b):
"""Return the sum of two numbers."""
return a + b

Triple quotes do not remove indentation or leading and trailing line breaks. If indentation affects the result, use textwrap.dedent() from the standard library.

3. Escape Sequences#

A backslash gives the following character a special meaning. These are some common escape sequences:

SequenceMeaning
\nNewline
\rCarriage return
\tHorizontal tab
\\One backslash
\'Single quote
\"Double quote
\bBackspace
\0Null character, with code point zero
\u4f60A Unicode character written with four hexadecimal digits—in this case, 你
print('Name: Bob\nAge: 18')

Output:

Name: Bob
Age: 18

4. f-Strings: The Preferred Choice for Modern Python#

Add f or F before a string to insert values or expressions inside {}. Python introduced f-strings in version 3.6:

name = 'Bob'
age = 18
text = f'Name: {name}, age: {age}'
print(text)

The braces can contain expressions, including function and method calls:

a = 10
b = 20
name = 'bob'
print(f'{a} + {b} = {a + b}') # 10 + 20 = 30
print(f'Name: {name.upper()}') # Name: BOB

A colon after the expression introduces a format specifier:

{expression:format_specifier}

4.1 Decimal Places, Thousands, and Percentages#

price = 12.3456
money = 1_234_567
rate = 0.856
print(f'{price:.2f}') # 12.35
print(f'{money:,}') # 1,234,567
print(f'{rate:.1%}') # 85.6%
  • .2f displays a fixed-point number with two decimal places.
  • , adds a thousands separator.
  • .1% multiplies the value by 100, keeps one decimal place, and adds a percent sign.

4.2 Padding Numbers with Zeros#

number = 7
print(f'{number:03d}') # 007

Here, d means a decimal integer, 3 sets the minimum field width, and 0 fills unused positions with zeros.

4.3 Alignment and Custom Fill Characters#

name = 'Bob'
print(f'|{name:<10}|') # Left-aligned
print(f'|{name:^10}|') # Centered
print(f'|{name:>10}|') # Right-aligned

Output:

|Bob |
| Bob |
| Bob|

You can place a fill character before the alignment symbol:

print(f'|{name:-<10}|') # |Bob-------|
print(f'|{name:-^10}|') # |---Bob----|
print(f'|{name:->10}|') # |-------Bob|

The pattern is fill character + alignment + field width. The symbols <, ^, and > mean left-aligned, centered, and right-aligned. Width is a minimum; longer values are not truncated.

This syntax can produce simple text tables:

print(f'{"Name":<10}{"Age":>5}')
print(f'{"Bob":<10}{18:>5}')
print(f'{"Alice":<10}{20:>5}')

Python measures width in characters, while terminals arrange text in display cells. CJK characters, full-width characters, and some emoji may occupy two cells, so perfect visual alignment is not guaranteed.

4.4 Printing Literal Braces#

Braces mark expressions inside an f-string. To print the braces themselves, double them as {{ or }}:

name = 'Bob'
print(f'{name}') # Bob
print(f'{{name}}') # {name}

4.5 Debugging Expressions#

Python 3.8 and later support = as a concise way to print both an expression and its value:

items = 3
price = 12.5
print(f'{items=}, {price=}, {items * price=}')

Output:

items=3, price=12.5, items * price=37.5

5. Raw Strings: The r Prefix#

The r or R prefix creates a raw string. Backslashes in its literal text are generally preserved:

normal = 'hello\nworld'
raw = r'hello\nworld'
print(normal)
print(raw)

Output:

hello
world
hello\nworld

Raw strings are especially useful for regular expressions:

import re
pattern = re.compile(r'\d+\.\d+')

They can also make Windows paths easier to read:

path = r'C:\Users\Bob\Desktop'

However, “raw” does not mean that backslashes play no role during parsing:

  • A raw string cannot end with an odd number of consecutive backslashes.
  • A quote matching the outer delimiter must still be protected with a backslash; that backslash remains in the result.
  • A raw string does not turn a real line break into the two characters \n.

The following code is invalid:

# SyntaxError: the string ends with a single backslash
path = r'C:\Users\Bob\'

If a path must end with a backslash, join it with a regular string. Better still, use pathlib.Path for file-system paths.

6. Byte Literals: The b Prefix#

The b or B prefix creates bytes, not str:

text = 'Hello'
data = b'Hello'
print(type(text)) # <class 'str'>
print(type(data)) # <class 'bytes'>

The two types serve different purposes:

  • str represents Unicode text.
  • bytes represents byte values from 0 to 255, used for network protocols, binary files, images, audio, compression, and cryptography.

Convert text to bytes with an explicit encoding, and decode the bytes with the matching encoding:

text = 'Hello, Python!'
data = text.encode('utf-8')
restored = data.decode('utf-8')
print(data) # b'Hello, Python!'
print(restored) # Hello, Python!

The source text of a byte literal may contain only ASCII characters. For example, b'你好' is invalid. Encode a str instead, or use \xhh escapes to specify exact byte values.

7. Unicode and the u Prefix#

Regular strings are already Unicode strings in Python 3, so these forms are equivalent:

text1 = u'Hello'
text2 = 'Hello'

The u prefix mainly remains for compatibility with older code that once supported Python 2 and Python 3. New Python 3 code rarely needs it.

8. Combining String Prefixes#

8.1 fr and rf#

The f and r prefixes can be combined. fr and rf are equivalent:

name = 'Bob'
path = rf'C:\Users\{name}\Desktop'
print(path) # C:\Users\Bob\Desktop

Here, f evaluates {name}, while r preserves backslashes in the literal parts. This is also useful for dynamic regular expressions:

import re
word = 'hello'
pattern = re.compile(rf'\b{re.escape(word)}\b')

Use re.escape() for dynamic input so that regex metacharacters in the value do not change the pattern’s meaning. The r prefix affects the literal parts of an f-string; it does not apply raw-string processing to values produced inside {}.

8.2 br and rb#

The b and r prefixes can also be combined. br and rb are equivalent:

data1 = br'\n'
data2 = rb'\n'
print(data1) # b'\\n'
print(data2) # b'\\n'

Each result contains two bytes—a backslash and the letter n—rather than a newline byte. Prefixes cannot be combined freely: an f-string produces text, so bf'...' and fb'...' are invalid.

9. Other Formatting Styles#

9.1 str.format()#

str.format() remains common in older projects and in code that reuses format templates:

name = 'Bob'
age = 18
text1 = 'Name: {}, age: {}'.format(name, age)
text2 = 'Name: {name}, age: {age}'.format(name=name, age=age)
price = 'Price: {:.2f}'.format(12.3456)

It supports essentially the same format-specifier language as f-strings.

9.2 % Formatting#

This is an older style of string formatting:

name = 'Bob'
age = 18
price = 12.3456
print('Name: %s, age: %d' % (name, age))
print('Price: %.2f' % price)

Common placeholders include %s for a string, %d for a decimal integer, %f for a floating-point number, and %x for a hexadecimal integer. For most new code, f-strings are clearer and more capable.

Python’s logging module is an important exception:

import logging
logging.info('User %s logged in', name)

This parameterized form lets the logging system delay interpolation until needed. Do not replace it with an f-string merely for consistency.

10. Everyday String Methods#

Strings are immutable. The following methods return new strings; they do not modify the original value.

Changing Case#

text = 'Hello World'
print(text.lower()) # hello world
print(text.upper()) # HELLO WORLD
print(text.title()) # Hello World

Removing Characters from the Ends#

text = ' hello '
print(text.strip())
print(text.lstrip())
print(text.rstrip())

With no argument, these methods remove whitespace. With an argument, they remove any combination of the characters in that argument—not one exact prefix or suffix.

Replacing Text#

text = 'Hello Bob'
result = text.replace('Bob', 'Alice')
print(result) # Hello Alice

Splitting and Joining#

text = 'apple,banana,orange'
fruits = text.split(',')
print(fruits) # ['apple', 'banana', 'orange']
print(','.join(fruits)) # apple,banana,orange

The separator calls join(), and every item in the iterable must be a string.

Testing Content#

text = 'Hello Python'
print(text.startswith('Hello')) # True
print(text.endswith('Python')) # True
print('Python' in text) # True

11. Choosing the Right Form#

Use caseRecommended form
Regular text'...' or "..."
Multiline text or a docstring'''...''' or """..."""
Values or expressions inside textf'...{value}...'
Regular expressionr'...'
Dynamic regular expressionrf'...', often with re.escape()
Binary datab'...' or str.encode()
File-system pathPrefer pathlib.Path
Parameterized logginglogging.info('... %s', value)

The main forms can be summarized in a few lines:

'hello' # Regular string
"hello" # Regular string
"""multiline string""" # Multiline string
f'hello {name}' # Formatted string
r'\d+\.\d+' # Raw string, often used for regex
b'hello' # Byte literal
rf'\b{re.escape(name)}\b' # Raw and formatted string

The choice becomes straightforward once you separate the decisions. First decide whether you are working with text or bytes. Then ask whether you need escapes, multiple lines, or interpolation. From there, choose the simplest form that expresses your intent clearly.

A Practical Guide to Python Strings: Quotes, Prefixes, and Formatting
https://astro-nyc.pages.dev/posts/python-strings-prefixes-formatting-guide/
Author
Hari
Published at
2026-09-04
License
CC BY-NC-SA 4.0