Skip to Content
BonicBot A2DevelopmentPython ProgrammingLesson 8 - Lists & String Manipulation

Lesson 8 - Lists & String Manipulation

Learning Objective

Create, index, and slice lists; use core list methods; and apply basic string methods (split, join, case conversion) to build formatted text from data.


Introduction

Lists let you group related values together instead of juggling separate variables for each one.

Strings, meanwhile, provide a rich set of built-in methods for splitting apart and reassembling text.

This lesson combines both ideas:

  • Store structured data in lists
  • Access and modify list contents
  • Split text into smaller pieces
  • Join pieces of text back together
  • Format data into human-readable output

These patterns appear everywhere in real-world programming, from processing CSV files to generating status reports and API responses.


Creating Lists

A list is an ordered collection of values.

students = ["Aditi", "Rahul", "Meera"] scores = [82, 91, 76]

Lists can store:

  • Strings
  • Numbers
  • Booleans
  • Other lists
  • Mixed data types

Example:

data = ["Python", 3.12, True]

Indexing Lists

Lists use zero-based indexing.

students = ["Aditi", "Rahul", "Meera"] print(students[0])

Output:

Aditi

Access the last item with negative indexing:

print(students[-1])

Output:

Meera

Slicing Lists

Slicing extracts part of a list.

print(students[0:2])

Output:

['Aditi', 'Rahul']

The slice:

[start:end]

includes start but excludes end.


Common List Methods


Code

# lists_and_strings.py students = ["Aditi", "Rahul", "Meera"] scores = [82, 91, 76] print(students[0], scores[0]) # indexing print(students[-1]) # negative indexing print(students[0:2]) # slicing students.append("Kabir") scores.append(88) print(students, scores) print("Number of students:", len(students)) # pairing list data with zip + a for loop for name, score in zip(students, scores): print(f"{name}: {score}") # basic string manipulation report_line = "Aditi,82,Pass" parts = report_line.split(",") print(parts) name, score_text, status = parts summary = f"{name.upper()} scored {score_text}{status}" print(summary) names_joined = ", ".join(students) print("Class roster:", names_joined)

Expected Output

Click to see expected output

Aditi 82 Meera ['Aditi', 'Rahul'] ['Aditi', 'Rahul', 'Meera', 'Kabir'] [82, 91, 76, 88] Number of students: 4 Aditi: 82 Rahul: 91 Meera: 76 Kabir: 88 ['Aditi', '82', 'Pass'] ADITI scored 82 — Pass Class roster: Aditi, Rahul, Meera, Kabir

Working with Multiple Lists Using zip()

The zip() function pairs items from multiple lists.

students = ["Aditi", "Rahul"] scores = [82, 91] for name, score in zip(students, scores): print(name, score)

Output:

Aditi 82 Rahul 91

This is a common way to process related data stored in separate lists.


Splitting Strings

The split() method breaks a string into a list.

report_line = "Aditi,82,Pass" parts = report_line.split(",")

Output:

['Aditi', '82', 'Pass']

The comma acts as the separator.


Joining Strings

The join() method combines multiple strings into one string.

students = ["Aditi", "Rahul", "Meera"] roster = ", ".join(students)

Output:

Aditi, Rahul, Meera

The separator appears between each item.


String Case Conversion

Convert text to uppercase:

name.upper()

Convert text to lowercase:

name.lower()

Example:

print("Python".upper()) print("Python".lower())

Output:

PYTHON python

String Slicing

Strings support indexing and slicing just like lists.

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

Output:

P n Pyt

This works because strings are ordered sequences of characters.


🔧 Under the Hood

Why does .append() change the list, but .upper() doesn’t change the string?

Lists are mutable.

This means the existing list object can be modified directly.

Example:

students = ["Aditi", "Rahul"] students.append("Kabir")

After the call:

['Aditi', 'Rahul', 'Kabir']

The original list itself changed.

Strings are immutable.

When you write:

name = "Aditi" name.upper()

Python creates a brand-new string:

"ADITI"

and returns it.

The original string remains unchanged:

name # "Aditi"

That’s why code such as:

summary = name.upper()

must use the returned value.

split() and join()

These methods are natural opposites.

Split:

"A,B,C".split(",")

Produces:

['A', 'B', 'C']

Join:

",".join(['A', 'B', 'C'])

Produces:

"A,B,C"

Shared Sequence Behavior

Both lists and strings support:

obj[0] obj[-1] obj[1:3]

because both are ordered sequences.


Student Challenge

Challenge 1

Write a function called is_palindrome(word) that checks whether a given string is a palindrome (reads the same forwards and backwards, such as "level" or "radar").

  • Convert word to lowercase using .lower() so the check is case-insensitive (e.g., "Kayak" should be recognized as a palindrome).
  • Compare the cleaned string to its reversed version using string slicing (word[::-1]).
  • Test your function by iterating over a list of words ["level", "python", "radar", "Kayak", "hello"] and printing whether each word is a palindrome.

Hint

This slice:

word[::-1]

creates a reversed copy of the string.

No loop is required.

Click to see solution

# palindromes.py def is_palindrome(word): cleaned = word.lower() return cleaned == cleaned[::-1] words = ["level", "python", "radar", "Kayak", "hello"] for word in words: result = ( "palindrome" if is_palindrome(word) else "not a palindrome" ) print(f"{word}: {result}")

Challenge 2

Drive a route stored as a list of tuples, log each action as a string, and compare the planned route against where the robot actually ended up.

Real wheels slip a little, so final_position will not perfectly match a hand-calculated expected position.

Full sensor method details are available in the Python SDK reference.

Hint

get_position() can return:

None

if odometry data has not yet arrived.

That’s why the challenge calls:

bot.wait_for_data()

before requesting the position.

Click to see solution

# challenge_route_log.py from bonicbot_bridge import BonicBot route = [ ("drive", 0.4), ("turn", 90), ("drive", 0.3), ("turn", -90), ] action_log = [] with BonicBot(host='192.168.0.188') as bot: bot.wait_for_data(timeout=3.0) for action, value in route: if action == "drive": bot.drive_distance(value, speed=0.2) action_log.append(f"drove {value} m") elif action == "turn": bot.rotate_angle(value, speed=45.0) action_log.append(f"turned {value}°") summary = "Route complete: " + ", ".join(action_log) + "." print(summary) final_position = bot.get_position() print(f"Final position: {final_position}")

Reflection Question

action_log is built up one string at a time inside the loop, then joined once at the end with ", ".join(...). Why is joining once at the end generally better practice than repeatedly gluing each new piece onto a growing string with += inside the loop?


By the end of this lesson, you should be able to create and modify lists, access elements with indexing and slicing, pair data using zip(), split and join strings, change text case, and combine list and string operations to produce formatted output.

Last updated on