Skip to main content
Python•Last Updated : 20 Oct 2026
~40 min read

Working with Strings in Python

Text in Python: string literals and escapes, indexing and slicing, immutability, the most useful string methods, and splitting and joining text.

Writing strings

A string (str) is an immutable sequence of characters. You write one between single or double quotes; both are identical, which lets you include the other kind of quote without fuss: "It's here" or 'He said "hi"'.

Inside a string, a backslash starts an escape sequence: \n is a newline, \t a tab, \\ a literal backslash and \" a quote. Triple quotes ("""...""") allow text that spans several lines. A raw string, r"...", treats backslashes as ordinary characters, which is handy for Windows paths and regular expressions.

len(text) returns the number of characters. The empty string "" has length 0.

PythonLiterals, escapes and length
1quote = 'She said "ready" and left.'
2print(quote)
3print("Line one\nLine two")
4print("Name:\tAda")
5path = r"C:\new\data"
6print(path, len(path))
7poem = """Roses are red,
8code is in Python."""
9print(poem)
10print(len(""), len("hello"))
Output
She said "ready" and left.
Line one
Line two
Name:	Ada
C:\new\data 11
Roses are red,
code is in Python.
0 5

Indexing: one character at a time

Each character has a position, its index, starting at 0. Negative indexes count from the end: -1 is the last character, -2 the one before it. Indexing past either end raises IndexError.

Zero-based counting feels odd at first, but it makes the arithmetic clean: the last valid index is always len(text) - 1, and text[-1] is a convenient way to say the same thing.

Indexes of "PYTHON"
 character   P   Y   T   H   O   N
 index       0   1   2   3   4   5
 negative   -6  -5  -4  -3  -2  -1
Every character has a positive and a negative index.
PythonPositive and negative indexes
1word = "PYTHON"
2print(word[0], word[1], word[5])
3print(word[-1], word[-2])
4print(word[len(word) - 1])
Output
P Y N
N O
N

Slicing: taking a piece

A slice text[start:stop] returns a new string from index start up to, but not including, stop. Leaving out start means "from the beginning" and leaving out stop means "to the end". The number of characters in text[a:b] is simply b - a.

An optional third value is the step: text[::2] takes every second character, and a negative step walks backwards, so text[::-1] is the reversed string.

Unlike indexing, slicing never raises an error for out-of-range positions; it just returns whatever part exists, possibly an empty string.

Tip

`text[:n]` and `text[n:]` always split a string into two parts that add back up to `text`, whatever the value of `n`.

PythonSlices
1text = "programming"
2print(text[0:7])
3print(text[3:])
4print(text[:3])
5print(text[-4:])
6print(text[::2])
7print(text[::-1])
8print(repr(text[20:30]))
Output
program
gramming
pro
ming
pormig
gnimmargorp
''

Strings cannot be changed in place

Strings are immutable. An assignment such as word[0] = "J" raises TypeError: 'str' object does not support item assignment. Every operation that seems to modify a string, upper(), replace(), slicing, +, actually builds and returns a new string and leaves the original alone.

So the result must be stored, usually by reassigning the name: word = word.upper(). Calling word.upper() on a line by itself computes a new string and then throws it away, a very common beginner bug.

PythonBuild a new string instead
1word = "python"
2word.upper()
3print(word)
4word = word.upper()
5print(word)
6fixed = "J" + word[1:]
7print(fixed)
Output
python
PYTHON
JYTHON

Joining, repeating and searching

+ concatenates two strings and * repeats a string a whole number of times. Both only work with strings: "Total: " + 5 is a TypeError, so convert numbers with str() or, better, use an f-string.

The in operator checks whether one string occurs inside another and returns a boolean. not in is its negation.

Python+, * and in
1first, last = "Grace", "Hopper"
2full = first + " " + last
3print(full)
4print("=" * len(full))
5print("Hop" in full, "hop" in full, "x" not in full)
6print("Items: " + str(3))
Output
Grace Hopper
============
True False True
Items: 3

The string methods you will use most

A method is a function attached to a value, called with a dot: text.upper(). Strings come with dozens; this handful covers most everyday work. None of them change the original string.

Common string methods

MethodWhat it returnsExample → result
lower() / upper()Lower- / upper-case copy"Hi".upper() → "HI"
title() / capitalize()Each word / only the first letter capitalised"ada lovelace".title() → "Ada Lovelace"
strip() / lstrip() / rstrip()Copy without surrounding whitespace (or given characters)" hi ".strip() → "hi"
replace(old, new)Copy with every occurrence replaced"a-b-c".replace("-", "") → "abc"
find(sub)First index of sub, or -1"banana".find("an") → 1
count(sub)Number of non-overlapping occurrences"banana".count("a") → 3
startswith(p) / endswith(s)True or False"data.csv".endswith(".csv") → True
isdigit() / isalpha() / isalnum()True if every character qualifies (False for "")"2026".isdigit() → True
PythonCleaning and inspecting text
1raw = " Ada LOVELACE "
2name = raw.strip().title()
3print(f"[{name}]")
4email = "Ada@Example.COM"
5print(email.lower(), email.find("@"), email.count("a"))
6print("report_2026.csv".endswith(".csv"), "2026".isdigit(), "20 26".isdigit())
7print("2026-10-20".replace("-", "/"))
Output
[Ada Lovelace]
ada@example.com 3 2
True True False
2026/10/20

Splitting text and joining pieces

text.split(sep) cuts a string at every sep and returns a list of the pieces. With no argument, split() splits on any run of whitespace and ignores leading and trailing spaces, which is usually what you want for words.

The reverse operation is sep.join(pieces): it glues a list of strings together with sep between them. Note that join is a method of the separator, and every piece must already be a string.

Caution

`"a b".split(" ")` returns `['a', '', 'b']`: splitting on a single space keeps empty pieces between repeated spaces. Use `split()` with no argument for words.

Pythonsplit() and join()
1sentence = " the quick brown fox "
2words = sentence.split()
3print(words)
4print(len(words), "words")
5print("-".join(words))
6row = "2026,10,20"
7print(row.split(","))
8print("".join(["P", "y", "t", "h", "o", "n"]))
9print(", ".join(str(n) for n in [1, 2, 3]))
Output
['the', 'quick', 'brown', 'fox']
4 words
the-quick-brown-fox
['2026', '10', '20']
Python
1, 2, 3

Walking through the characters

A string is a sequence, so a for loop visits its characters one by one. (Loops have their own lesson; this is a first look.) Combining a loop with the methods above is how most text processing works: counting, filtering, building a new string.

Every character also has a numeric code point: ord("A") is 65 and chr(65) is "A". Python strings are Unicode, so they hold accented letters and scripts from every language, not only English letters.

PythonCounting and filtering characters
1text = "Order 66 shipped on 2026-10-20"
2digits = ""
3letters = 0
4for ch in text:
5 if ch.isdigit():
6 digits += ch
7 elif ch.isalpha():
8 letters += 1
9print(digits)
10print(letters, "letters")
11print(ord("A"), chr(66), ord("a") - ord("A"))
Output
6620261020
14 letters
65 B 32

Check your understanding

Suggested Quiz
Question 1 of 5

What does `print("python"[1:4])` print?

Practice

Start with initials from a name (split, indexing, join), then answer six questions about a piece of text with slices, methods and a counting loop.

[Problem]

Name Initials

Reveal Solution & Analysis
Model Solution
[Problem]

String Manipulation Mastery

Reveal Solution & Analysis
Model Solution
[Problem]

Run-Length Encoding

Reveal Solution & Analysis
Model Solution

Quick Syntax & Reference Sheet

Concept / RuleSyntax / Usage
Lengthlen(text)
First / last charactertext[0], text[-1]
Slice (stop excluded)text[2:5], text[:3], text[3:]
Reversetext[::-1]
Clean user inputtext.strip().lower()
Words of a sentencesentence.split()
Join with a separator", ".join(items)
Substring test"@" in email

Common Pitfalls & Developer Mistakes

✕ Mistake 1:

Calling a method without storing the result

Why it happens: Strings are immutable, so text.strip() on its own changes nothing.

Correction: text = text.strip()
✕ Mistake 2:

Off-by-one slices

Why it happens: The stop index is excluded: text[0:3] has three characters, indexes 0, 1 and 2.

Correction: Remember that text[a:b] has b - a characters.
✕ Mistake 3:

Using split(" ") to get words

Why it happens: Repeated spaces produce empty strings in the result.

Correction: Use split() with no argument.
✕ Mistake 4:

Joining non-strings

Why it happens: ", ".join([1, 2]) raises TypeError because join needs strings.

Correction: ", ".join(str(n) for n in numbers)

Frequently Asked Questions

Is there a character type in Python?

No. A single character is just a string of length 1, so text[0] is a str.

find() or index()?

Both return the position of a substring. find() returns -1 when it is missing; index() raises ValueError. Use find() when absence is normal and index() when absence is a bug.

Why is building a string with += in a loop discouraged?

Each += creates a new string, so very long loops do a lot of copying. For many pieces, collect them in a list and call "".join(pieces) once.

How do I compare strings without caring about case?

Normalise both sides first: a.lower() == b.lower(). For international text, a.casefold() == b.casefold() handles more cases.

Hands-On Practice Problems

Article Tags:#Python#Strings#Slicing#String Methods#Text Processing
Was this lesson clear and helpful?

Discussion (0)

Sign in to ask questions and reply.
Loading discussion…
Discuss in community