A space club has badge names, coded signals, and messages to inspect. Your challenge is to reach the exact characters you need, clean messy text, and build tools that transform or search a message.
By the end, you will build a secret-message shifter, a position tool, and an alphabet scan report.
You will learn
- Find a character by its position and take part of a string with a slice.
- Clean, test, split, and join text with string methods.
- Check whether a character appears in text with
in. - Build a new string one character at a time and search for a match.
Lesson 1: Reach into a String
Build badge initials
A string is a sequence of characters. Each character has a numbered position called an index. Python starts numbering at 0, so [0] reaches the first character. Reach the first character of each name to build initials.
first_name = "Maya"
last_name = "Chen"
initials = first_name[0] + last_name[0]
print(initials)
Output
MCNotice
The initials are MC. The brackets reach one character, so first_name[0] is itself a one-character string. Meaningful names such as first_name and initials help a reader understand the job.
Now reach the last character with index -1. Negative indexes count backward from the end.
print(first_name[-1])
Output
aNotice
-1 selects a, the last character of Maya. Positive indexes still count from the front.
Cut out a substring with a slice
A slice reaches a section of a string. In text[a:b], position a is included and position b is excluded. This is the same stop rule used by range(a, b).
signal = "ORBIT-739"
word_part = signal[0:5]
number_part = signal[6:9]
print(word_part)
print(number_part)
Output
ORBIT
739Visible payoff: the two lines print ORBIT and then 739. The first slice includes positions 0 through 4 but stops before 5. The second includes positions 6 through 8 but stops before 9. A slice is still a string, even when it contains only part of the original string.
Leave one slice end open
An omitted start begins at the first character. Try word[:3].
word = "galaxy"
print(word[:3])
Output
galNotice
word[:3] prints gal; position 3 is excluded.
An omitted stop continues to the end. Try word[3:].
print(word[3:])
Output
axyNotice
word[3:] prints axy. The two open-ended slices cover the whole word.
Reverse with one special slice
The reversing slice [::-1] reads a string from its last character back to its first.
print(word[::-1])
Output
yxalagNotice
The result is yxalag. This is the only step slice used in this unit.
Recall len from Unit 07
len(text) counts the characters in a string, as it did in Unit 07. Spaces and punctuation are characters too. Measure a badge label next.
call_sign = "Blue Fox"
size = len(call_sign)
print(f"Badge text: {call_sign}")
print("Characters: " + str(size))
Output
Badge text: Blue Fox
Characters: 8The f-string places the string value directly in a report. The next report uses str(size) to convert the integer 8 into text before concatenating with +.
Lesson 2: Clean and Test
Repair a messy signal
A control panel receives extra spaces and mixed capital letters. String methods are small tools attached to a string value. Start with upper(), which makes uppercase text.
signal = "Red Comet"
print(signal.upper())
Output
RED COMETNotice
upper() prints RED COMET and does not change signal.
Now use lower() to make a separate lowercase version.
print(signal.lower())
Output
red cometNotice
The result is red comet; the saved signal is still Red Comet.
Extra spaces at the beginning and end of a received line can be removed with strip().
raw_signal = " RED COMET "
print(raw_signal.strip())
Output
RED COMETNotice
The printed text has no spaces at either end. strip() leaves internal spaces in place.
To change a matching piece of text, use replace(old, new).
print("red comet".replace("red", "blue"))
Output
blue cometNotice
The result is blue comet; the old text was not edited in place.
Now chain the four methods to clean, change, and announce one signal.
raw_signal = " RED COMET "
clean_signal = raw_signal.strip().lower()
changed_signal = clean_signal.replace("red", "blue")
loud_signal = changed_signal.upper()
print(clean_signal)
print(changed_signal)
print(loud_signal)
Output
red comet
blue comet
BLUE COMETNotice
The chain prints red comet, blue comet, and BLUE COMET. strip() removes spaces only at the ends, and every method returns a new string. The original raw_signal stays unchanged. Because strip() returns a string, you can call .lower() on that result right away.
Find and recognize text
find(piece) reports the first matching position, or -1 when no match exists.
message = "moon rover"
print(message.find("rover"))
print(message.find("sun"))
Output
5
-1Notice
The positions are 5 and -1. A missing piece has no string index.
startswith checks the beginning; endswith checks the end. Both return a boolean.
filename = "mission.py"
print(filename.startswith("mission"))
print(filename.endswith(".py"))
Output
True
TrueNotice
Both checks are True; the methods test different ends of the same string.
isdigit() checks that every character is a digit; isalpha() checks that every character is a letter.
print("4071".isdigit())
print("orbit".isalpha())
print("-3".isdigit())
Output
True
True
FalseNotice
The first two checks are True; "-3".isdigit() is False, so this simple digit check does not accept negative numbers.
Join characters and walk words
A separator string can join the characters of another string.
print("-".join("abc"))
Output
a-b-cNotice
The result is a-b-c. The separator goes between characters, with none at either end.
split() lets a loop visit words separated by whitespace. Walk the words directly, without indexing the collection that split() returns.
line = "stars need fuel"
for word in line.split():
print(word)
Output
stars
need
fuelNotice
The loop prints stars, need, and fuel on separate lines. Each word is a string, so word[0] may reach its first character.
Test membership
The in operator asks whether a character or substring appears in a string. It returns a boolean. Matching is case-sensitive, so cleaning case first can make a test dependable. The two tests below print True and then False.
message = "Launch at Dawn"
quiet_message = message.lower()
print("dawn" in quiet_message)
print("moon" in quiet_message)
Output
True
FalseTry real input: read a PIN and check whether it has exactly four digits.
Try it yourself
pin = input("PIN: ")
if len(pin) != 4:
print(False)
elif not pin.isdigit():
print(False)
else:
print(True)
Notice
len checks the width and isdigit() rejects letters; this check also rejects a leading minus sign.
Test a palindrome from both ends
A palindrome reads the same from left to right and right to left. Before checking both ends, walk through the positions of one short word.
w = "level"
for i in range(len(w)):
print(i, w[i])
Output
0 l
1 e
2 v
3 e
4 lNotice
The loop reports positions 0 through 4 and their characters. An index loop gives both a position and the character at that position.
Next compare just one mirror pair: the first and last characters.
w = "level"
i = 0
print(w[i] == w[len(w) - 1 - i])
Output
TrueNotice
This one pair matches. A full palindrome check repeats the mirror comparison for half the word.
Now clean spaces and case, compare every mirror pair, and leave early at a mismatch.
def is_palindrome(text):
cleaned = text.lower().replace(" ", "")
for i in range(len(cleaned) // 2):
if cleaned[i] != cleaned[len(cleaned) - 1 - i]:
return False
return True
print(is_palindrome("Never odd or even"))
print(is_palindrome("space camp"))
Output
True
FalseFor each loop counter i, the left position is i and the matching right position is len(cleaned) - 1 - i. A mismatch returns False immediately. return True belongs after the loop because every tested pair must match first.
Try real input: read a word or phrase and ask the palindrome function about it.
Try it yourself
word = input("Word: ")
print(is_palindrome(word))
Notice
The typed text replaces the fixed sample; the function returns one boolean.
Count vowels by condition
A counter can grow only when the current character passes a membership test. The report below prints Vowels: 5.
A for loop can walk directly through a string: for ch in text: gives each character in order. This is a value loop: use it when the character itself matters. An index loop such as for i in range(len(text)): is different: it gives positions, as in the palindrome example above and the position example later.
word = "sun"
for ch in word:
print(ch)
Output
s
u
nNotice
The loop prints s, u, and n in order. It visits characters directly, without using their indexes.
Now count only characters that are vowels, using a condition inside the character loop.
def vowel_count(text):
total = 0
cleaned = text.lower()
for ch in cleaned:
if ch in "aeiou":
total = total + 1
return total
amount = vowel_count("Solar eclipse")
print(f"Vowels: {amount}")
Output
Vowels: 5Choose among three character kinds
Membership tests also fit an if / elif / else branch. Test the narrow vowel case first, the wider alphabet case second, and everything else last. The three calls below print vowel, consonant, and other.
def character_kind(ch):
letter = ch.lower()
if letter in "aeiou":
return "vowel"
elif letter in "abcdefghijklmnopqrstuvwxyz":
return "consonant"
else:
return "other"
print(character_kind("E"))
print(character_kind("z"))
print(character_kind("?"))
Output
vowel
consonant
otherLesson 3: Transform and Search
Build a new string one character at a time
A transmission effect needs every character doubled. Strings can be accumulated with +: begin with an empty string, transform each character, and save the growing result.
def double_letters(text):
transformed = ""
for ch in text:
transformed = transformed + ch + ch
return transformed
print(double_letters("Go!"))
print(double_letters("42"))
Output
GGoo!!
4422Visible payoff: the calls print GGoo!! and then 4422. The accumulator transformed starts empty. Each loop trip adds the current character twice. The function returns a new string; it does not change text. This is the transform-each pattern.
Try real input: read one line, count its words, and print the count.
Try it yourself
line = input("Line: ")
count = 0
for word in line.split():
count = count + 1
print(f"Words: {count}")
Notice
Each word contributes one to the count, including words after extra spaces.
Search from left to right
A navigation display needs the first position of one character. Visit positions in order. Return the loop counter as soon as the character matches, or return -1 only after the whole loop ends.
def position(text, ch):
for i in range(len(text)):
if text[i] == ch:
return i
return -1
print(position("meteor", "e"))
print(position("meteor", "x"))
Output
1
-1The first call returns the integer 1. The second returns -1, the signal that no position matched. Returning inside the loop preserves the first match. Returning -1 inside the loop would stop too early. The short report below prints Saved position: 4.
saved_position = position("satellite", "l")
report = "Saved position: " + str(saved_position)
print(report)
Output
Saved position: 4Count one chosen character
Searching for the first match and counting every match are related but different jobs. For a character count, keep walking after a match and update an integer counter. The two calls below print 2 and then 0.
def count_char(text, ch):
matches = 0
for current in text:
if current == ch:
matches = matches + 1
return matches
print(count_char("balloon", "l"))
print(count_char("balloon", "z"))
Output
2
0Print an alphabet scan table
A report can scan the alphabet one letter at a time. The outer loop chooses a letter, and count_char scans the message for that letter. print writes one table row per alphabet letter.
def print_letter_table(text):
alphabet = "abcdefghijklmnopqrstuvwxyz"
cleaned = text.lower()
print("letter | total")
for letter in alphabet:
total = count_char(cleaned, letter)
print(f"{letter} | {total}")
print_letter_table("Cabana!")
Output
letter | total
a | 3
b | 1
c | 1
d | 0
e | 0
f | 0
g | 0
h | 0
i | 0
j | 0
k | 0
l | 0
m | 0
n | 1
o | 0
p | 0
q | 0
r | 0
s | 0
t | 0
u | 0
v | 0
w | 0
x | 0
y | 0
z | 0The table always has one row for each of the 26 lowercase letters. For Cabana!, the nonzero rows are a | 3, b | 1, c | 1, and n | 1; all other rows show 0. It reports every total without choosing a winning letter. The loops make each scan step visible.
Challenge: Shift a message
A Caesar shift moves each letter forward through the alphabet. Build the search helper first. Then lowercase the message, search the alphabet for each character, and use remainder arithmetic to wrap from z to a. Nonletters pass through unchanged. Here k >= 0.
def position(alphabet, ch):
for i in range(len(alphabet)):
if alphabet[i] == ch:
return i
return -1
def shift(message, k):
alphabet = "abcdefghijklmnopqrstuvwxyz"
shifted = ""
cleaned = message.lower()
for ch in cleaned:
pos = position(alphabet, ch)
if pos == -1:
shifted = shifted + ch
else:
new_pos = (pos + k) % 26
shifted = shifted + alphabet[new_pos]
return shifted
print(shift("Meet at 8!", 3))
print(shift("Zebra", 2))
Output
phhw dw 8!
bgdtcChallenge payoff: the calls print phhw dw 8! and then bgdtc.
Put it together: indexing reaches one character, and slices reach a section or reverse a string. The string methods taught here clean, inspect, and join text; in tests membership. Counter loops measure matches, transform-each loops build new strings, and a left-to-right search returns a position or -1.
Recap
text[0]reaches the first character;text[1:4]takes positions 1 through 3.- String methods can change case, trim spaces, replace text, or split a line into words.
intests whether a character or smaller string appears in a string.- A loop can add transformed characters to a new string without changing the original.
- A search checks positions in order and can return the match's position or
-1.
You can now inspect, clean, transform, and search the space club's messages.