Python String Indexing

Introduction: Strings as Ordered Sequences

Welcome to Manipulating Strings in Python! Text shows up everywhere in real programs: usernames on a login screen, filenames in a folder, product codes on an invoice. Before we can clean, split, or reformat any of that text, we need one basic skill: reaching a single character inside a string.

A Python string is an ordered sequence of characters. "Ordered" means the characters have fixed positions that never shuffle on their own, and each position carries a number we can use to pull that character out. Our running example is a simple username:

Python
username = "codesignal"

Positive Indices Start at Zero

To reach one character, we write the variable name followed by square brackets containing a position number: string[index]. Python counts positions starting from 0, not from 1 — the single most common source of confusion for beginners. Each character has an index, a number representing its position that we can use to access that character.

Charactercodesignal
Index0123456789

A ten-character string has indices 0 through 9, never 10. The mental shift to make is that the third character sits at index 2, because counting begins one step earlier than we say it out loud.

Accessing Characters by Position

Let's put the bracket syntax to work:

Python
username = "codesignal"

# Positive indices start at 0 from the left
print("First character:", username[0])
print("Third character:", username[2])

Reading the table above, username[0] lands on "c" and username[2] lands on "d". One detail worth locking in: indexing gives us back a string of length one, not some special "character" type. The result behaves like any other string and can be printed, compared, or joined with other text.

text
First character: c
Third character: d

Negative Indices Count from the End

Often we care about the end of a string without knowing how long it is: the last letter of a name, the final digit of an order code. Counting forward would force us to measure first. Python solves this with negative indices, which count backward from the right: -1 is the last character, -2 the second-to-last, and so on.

Charactercodesignal
Positive index0123456789
Negative index-10-9-8-7-6-5-4-3-2-1

The asymmetry is worth memorizing: positive indices start at 0, negative indices start at -1. There is no -0, since 0 already means the first character.

Grabbing the Last Character

With negative indexing available, the final character takes one short expression:

Python
# Negative indices count from the right, starting at -1
print("Last character:", username[-1])

Here username[-1] reaches the rightmost character of "codesignal", which is "l". The nice part is that this expression works unchanged for any non-empty string, whether it holds three characters or three hundred — we never have to measure anything first.

text
Last character: l

Measuring Length with len()

Sometimes we genuinely need to know how long a string is: to check whether a password is long enough, or to find a midpoint. Python's built-in len() function returns the number of characters:

Python
# len() gives the number of characters
length = len(username)
print("Length:", length)

We pass the string into len() and store the answer in a variable so we can reuse it. Two clarifications:

  • len() counts every character, including spaces, digits, and punctuation, so "hi there!" has length 9.
  • len() returns a count, not an index; the count for "codesignal" is 10, even though the highest valid index is 9.
text
Length: 10

Connecting len() and Indexing

That last point gives us the rule that ties both ideas together: for any string, the valid positive indices run from 0 up to len(string) - 1. So we can reach the final character with arithmetic instead of a negative index:

Python
# The last character can also be reached with len() - 1
print("Last via len():", username[length - 1])

Since length holds 10, this evaluates to username[9], which is "l" — exactly what username[-1] gave us. The - 1 is not optional: username[10] would point one step past the end and raise an IndexError.

Every index expression requires the character it asks for to actually exist. That is why an empty string "" has no valid indices at all — not even 0 or -1 — and every one of these lookups would raise IndexError on it. Throughout this unit we work with non-empty strings, so we are safe; later, when values come from real user input, checking that a string has content first becomes part of the job.

text
Last via len(): l

Reaching the Middle with Floor Division

Now for a position that truly needs the length: the middle. Dividing with / produces a decimal such as 5.0, and indices must be whole numbers. So we use //, the floor division operator, which divides and discards any fractional part:

Python
# Floor division keeps the index a whole number
print("Middle character:", username[length // 2])

With length equal to 10, the expression 10 // 2 gives 5, so we ask for username[5], which is "i".

Be precise about what "middle" means here, because it depends on the length:

  • Odd length, such as "cat": there is one true centre, and 3 // 2 gives 1, hitting "a" exactly.
  • Even length, such as our ten-character username: there are two central characters, s at index 4 and i at index 5. The len(s) // 2 pattern always picks the right-hand one of the pair.

That right-hand choice is the convention we will use for the rest of the course, so "codesignal" gives "i" rather than "s".

text
Middle character: i

The Complete Program

Every piece we built, gathered into one file — this is the program the practices will walk you through building yourself:

Python
username = "codesignal"

# Positive indices start at 0 from the left
print("First character:", username[0])
print("Third character:", username[2])

# Negative indices count from the right, starting at -1
print("Last character:", username[-1])

# len() gives the number of characters
length = len(username)
print("Length:", length)

# The last character can also be reached with len() - 1
print("Last via len():", username[length - 1])

# Floor division keeps the index a whole number
print("Middle character:", username[length // 2])

Conclusion and Next Steps

Four tools, one habit: pick a position, put it in brackets. Positive indices count from 0, negative indices count from -1, len() measures, and len(s) - 1 and len(s) // 2 land on the last and middle positions of a non-empty string.

In Unit 2 we scale up from single characters to whole chunks of text with slicing. First, let's make these positions automatic — the practices are waiting.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal