Unit 08 · lesson

Strings Are Data Structures Too

Core path: 30 minutes

You already know strings as text. What changes when you start treating the text as an ordered sequence?

A string is not one indivisible blob to Python. It is an ordered sequence of characters.

That means characters have positions, slices can select ranges, loops can visit characters one at a time, and invalid positions can fail.

Position starts at zero

Take:

callsign = "NOVA"

The characters are ordered:

character:  N   O   V   A
index:      0   1   2   3

So:

print(callsign[0])

produces:

N

and:

print(callsign[3])

produces:

A

The first position is index 0, not 1.

That one rule creates a lot of beginner off-by-one bugs because humans usually count the first item as "one."

word = "PYTHON"
print(len(word))

Output:

6

There are six characters.

The valid indexes are:

0 1 2 3 4 5

So the last index is:

len(word) - 1

not:

len(word)

This fails:

print(word[6])

because there is no character at index 6.

Python raises IndexError because the requested position is outside the sequence.

Slicing selects a range between boundaries

word = "ROBOTNIX"
print(word[0:5])

produces:

ROBOT

The slice syntax is:

[start:stop]

The start index is included.

The stop index is excluded.

So 0:5 selects indexes:

0 1 2 3 4

and stops before index 5.

Python sequence lab

Move the slice boundaries

Python includes the start index and excludes the stop index. Change both values and watch the selected characters update.

Try 0:5, 2:8, 3:3, and a start value greater than the stop value.

Indexed sequence
0R
1O
2B
3O
4T
5N
6I
7X
8stop boundary
Current expressionword[0:5]
Observed result"ROBOT"

Selected indexes satisfy start ≤ index < stop. The stop boundary itself is never included.

Use the sequence explorer instead of memorizing the rule.

Try:

0:5
2:8
3:3
7:2

Before changing the controls, predict which characters should be highlighted.

For 3:3, there is no distance between the start and stop boundaries, so the result is an empty string.

For 7:2, the default forward slice direction cannot travel backward from start 7 to stop 2, so the result is also empty unless you explicitly use a negative step.

That is a useful reminder: slice boundaries describe a traversal, not just two random indexes.

Negative indexes count from the end

Python also supports:

word = "ROBOTNIX"
print(word[-1])

which produces the last character:

X

Useful examples:

-1 → last item
-2 → second from last

Do not mix negative indexing into every beginner solution just because it is shorter. Use it when it makes the intent clearer.

Strings are immutable

This works:

name = "nova"
name = name.upper()

This does not:

name = "nova"
name[0] = "N"

Python strings cannot be changed character-by-character in place.

Methods such as:

lower()
upper()
strip()
replace()

produce new string values.

So:

name.upper()

by itself does not permanently change what name refers to.

If you want the transformed string later:

name = name.upper()

A loop can traverse the sequence without indexes

for character in "nova":
    print(character)

Output:

n
o
v
a

You do not always need an index to process sequence items.

Use an index when position matters.

Use direct iteration when you mainly need each item.

That is a cleaner decision than automatically writing range(len(...)) for every loop.

Derive the index instead of guessing

callsign = "GHOST"

Before running anything:

length = 5
first index = 0
last index = 4

Now verify:

print(len(callsign))
print(callsign[len(callsign) - 1])

Then deliberately create:

print(callsign[len(callsign)])

Read the resulting IndexError.

The failure is not mysterious. Your calculated position was one step beyond the valid sequence.

Build three useful slices

Given:

filename = "robot_status.json"

Without searching the internet for the answer, use indexing/slicing/string methods to reason about:

  • the first five characters;
  • the final four characters;
  • whether the filename ends with .json.

Python has useful methods such as .endswith() that may be clearer than manually slicing every time. The point is to understand the sequence model well enough to choose the readable operation.

Carry the Week 5 loop model forward

This is the connection:

Week 5: loop over repeated values
Week 8: strings/lists provide ordered values to loop over

The loop did not change.

The thing being iterated over changed.

That is how programming concepts start combining instead of living in separate weeks.

Vocabulary lab

Flip the idea, not just the card

Explain the term before you reveal the back. Then compare your explanation with the definition, example, and warning.

1 / 5
Read all terms without animation
Sequence
An ordered collection whose items can be processed in a defined order. Example: A string is a sequence of characters. Do not confuse it with: A single scalar value with no sequence positions.
Index
A numeric position used to select one item from a sequence. Example: word[0] accesses the first character. Do not confuse it with: A dictionary key used as a semantic label.
Zero-Based Indexing
A position system in which the first item is index 0. Example: The first character of PYTHON is at index 0 and the sixth is at index 5. Do not confuse it with: Human ordinal counting that often begins at 1.
Slice
A selection of sequence items between start and stop boundaries, with the stop normally excluded. Example: word[0:5] selects indexes 0 through 4. Do not confuse it with: Selecting exactly one item with word[0].
Immutable
Unable to be modified in place after creation. Example: A Python string cannot replace one character with name[0] = 'N'. Do not confuse it with: A list, whose items can be replaced in place.