Skip to content

Strings

A string is text — a sequence of characters. In Python you can use single or double quotes.

Creating strings

a = "hello"
b = 'world'
c = """A multi-line
string with line
breaks."""

print(a, b)
print(c)

Accessing characters by index

Strings are indexed from 0. Negative indices count from the end.

word = "Python"

print(word[0])      # 'P' — first char
print(word[1])      # 'y'
print(word[-1])     # 'n' — last char
print(word[-2])     # 'o'
print(len(word))    # 6 — length

Slicing — substrings

string[start:stop] gives you characters from start (inclusive) to stop (exclusive).

word = "Python"

print(word[0:3])     # 'Pyt'
print(word[2:5])     # 'tho'
print(word[:3])      # 'Pyt' — from start
print(word[3:])      # 'hon' — to end
print(word[-3:])     # 'hon' — last 3
print(word[::2])     # 'Pto' — every 2nd char
print(word[::-1])    # 'nohtyP' — reversed!

Strings are immutable

You cannot change a character in place:

word = "Python"
# word[0] = "J"     # ← TypeError: strings are immutable

# Instead, create a new string
new_word = "J" + word[1:]
print(new_word)    # 'Jython'

Common string methods

s = "  Hello, World!  "

print(s.lower())        # '  hello, world!  '
print(s.upper())        # '  HELLO, WORLD!  '
print(s.strip())        # 'Hello, World!' — removes outer whitespace
print(s.lstrip())       # left strip only
print(s.rstrip())       # right strip only
print(s.replace("World", "Python"))   # '  Hello, Python!  '
print(s.title())        # '  Hello, World!  '
print(s.capitalize())   # '  hello, world!  '
print(s.swapcase())     # '  hELLO, wORLD!  '

Searching in strings

s = "Python is awesome and Python is easy"

print("Python" in s)         # True
print("Java" in s)           # False

print(s.find("is"))          # 7  — first occurrence
print(s.find("missing"))     # -1 — not found
print(s.index("is"))         # 7  — like find but raises if missing
print(s.count("Python"))     # 2  — number of occurrences
print(s.startswith("Py"))    # True
print(s.endswith("easy"))    # True

Splitting and joining

# split — string → list
csv = "apple,banana,cherry"
fruits = csv.split(",")
print(fruits)              # ['apple', 'banana', 'cherry']

# split by whitespace (default)
sentence = "Python is great"
words = sentence.split()
print(words)               # ['Python', 'is', 'great']

# join — list → string
parts = ["2025", "01", "31"]
date = "-".join(parts)
print(date)                # '2025-01-31'

# Lines
text = "line1\nline2\nline3"
print(text.splitlines())   # ['line1', 'line2', 'line3']

Checking string content

print("abc123".isalnum())     # True  — letters or digits
print("abc".isalpha())        # True  — only letters
print("123".isdigit())        # True  — only digits
print("hello".islower())      # True
print("HELLO".isupper())      # True
print("   ".isspace())        # True
print("Hello World".istitle())  # True

String formatting — f-strings (modern)

name = "Alice"
age = 25
balance = 1234.5678

print(f"{name} is {age}")                  # Alice is 25
print(f"Balance: ${balance:.2f}")          # Balance: $1234.57
print(f"Padded:    {age:>5}")              # right-aligned in 5 chars
print(f"Filled:    {age:0>5}")             # zero-padded:  00025
print(f"Centered:  {name:^10}")            # centered in 10 chars
print(f"Binary:    {255:b}")               # 11111111
print(f"Hex:       {255:x}")               # ff
print(f"Percent:   {0.875:.1%}")           # 87.5%
print(f"Scientific:{12345:.2e}")           # 1.23e+04

Escape characters

Some special characters need a backslash:

print("Hello\nWorld")        # \n = newline
print("Col1\tCol2\tCol3")    # \t = tab
print("She said \"hi\"")     # \" inside double quotes
print("Path: C:\\Users")     # \\ = literal backslash

# Raw string — backslashes are literal
print(r"Path: C:\Users\Alice")    # no escape needed

Repeating + concatenating

print("ha" * 3)              # 'hahaha'
print("=" * 20)              # '====================='

print("Hello, " + "World")   # 'Hello, World'

Iterating over a string

for letter in "Python":
    print(letter)

# Count vowels
text = "Hello, World!"
vowels = sum(1 for ch in text.lower() if ch in "aeiou")
print(f"{vowels} vowels")

Mini-project — Palindrome checker

A palindrome reads the same forwards and backwards.

def is_palindrome(text):
    clean = text.lower().replace(" ", "")
    return clean == clean[::-1]

print(is_palindrome("racecar"))                   # True
print(is_palindrome("A man a plan a canal Panama"))  # True
print(is_palindrome("python"))                    # False

Practice

What does this print?

Expected: nohtyP

word = "Python"
print(word[::-1])

Print 'hello, world' in all lowercase, no extra spaces

Expected: hello, world

s = "  Hello, World!  "
print(s.lower())          # bug: still has leading/trailing spaces and "!"

Quiz — Quick check

What you remember

Q1. What does "banana".count("a") return?

  • 1
  • 2
  • 3
  • "a"

Why: count returns the number of non-overlapping occurrences of the substring. "b-A-n-A-n-A" has three as.

Q2. Which of these makes word lowercase in place?

  • word.lower()
  • word = lower(word)
  • None of the above — strings are immutable; you must reassign with word = word.lower()
  • word.lowercase()

Why: Strings can't be mutated. Every "string method" returns a new string; you have to assign it back if you want the original variable to change.

Q3. What does "a,b,,c".split(",") produce?

  • ["a", "b", "c"]
  • ["a", "b", "", "c"]
  • ["a,b,c"]
  • ["a", "b", "c", ""]

Why: split preserves empty strings between consecutive delimiters. To drop empties, filter the result or use split() without an argument (which collapses whitespace).

Common doubts

Why does s[0] = 'x' give a TypeError?

Strings are immutable — you can't change them in place. Build a new string instead: s = 'x' + s[1:]. The same rule applies to tuples; lists and dicts are mutable.

Difference between find and index?

Both return the position of a substring. find returns -1 if not found; index raises ValueError. Use find when "not found" is expected; use index when you want a hard error to surface a bug.

When should I use an f-string vs .format()?

Always default to f-strings. They're faster, shorter, and the variable names appear right where they're used. The only place you can't use f-strings is when the template comes from elsewhere (config, database) — there you need .format(**values).

What's next

Lists