📌 Snapshot
- Predict every string output in CUET: indexing, slicing with steps, immutability and the key string methods.
- How a string is indexed forwards and backwards, why it cannot be edited in place, and how slices are counted.
- A method table grouped by job, plus traced programs for counting, replacing, reversing and palindromes.
- Traps NTA loves: slice stop values, find versus index, methods that return a new string, and case in palindromes.
📖 Detailed Notes
Written and reviewed by Rohit Singh, Computer Science & Commerce faculty at UniDrill (MCA, Osmania University · 6 years teaching). Based on NCERT Computer Science Class XI, Chapter 8.
Why can't you fix one wrong letter in a string?
You type Hello and realise you wanted Jello. In a word processor you would just replace the first letter. In Python, s[0] = 'J' fails with an error. That single surprise explains half of this chapter: a string is a sequence you can read letter by letter, but never edit in place. Everything else here (slicing, methods, building new strings in loops) is a consequence of that one rule.
What is a string?
A string is a sequence of one or more Unicode characters. A character can be a letter, a digit, a space or any symbol. You create a string by enclosing characters in single, double or triple quotes. Triple quotes let the string run over several lines. Python has no separate character type: a string of length one is simply a character.
| Code |
|---|
str1 = 'Hello World!' |
str2 = "Hello World!" |
str3 = """Hello World! |
welcome to the world of Python""" |
All of str1 and str2 hold the same value. Only the triple quoted str3 spreads across two lines.
Indexing: every character has two addresses
Each character has a position called its index. From the left, the first character has index 0 and the last has index $n - 1$, where $n$ is the length. From the right, the last character is $-1$ and the first is $-n$. For 'Hello World!' (length 12, the space counts):
| Character | H | e | l | l | o | (space) | W | o | r | l | d | ! |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Positive index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Negative index | -12 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
The built in len(str1) gives 12. So str1[0] is 'H', str1[6] is 'W', str1[-1] is '!', and str1[len(str1) - 1] is also '!'. The index can be any expression that evaluates to an integer, so str1[2 + 4] is 'W'.
| Mistake | What Python does |
|---|---|
str1[15] (outside 0 to 11) |
IndexError: string index out of range |
str1[1.5] (a float) |
TypeError: string indices must be integers |
A string is immutable
Immutable means the contents cannot change after the string is created. Trying str1[1] = 'a' raises TypeError: 'str' object does not support item assignment. If you need a changed version, you build a new string and give it a name.
Worked example: turn 'Hello' into 'Jello'.
| Step | Code | What happens |
|---|---|---|
| 1 | s = 'Hello' |
The string exists |
| 2 | s[0] = 'J' |
Fails: TypeError, strings cannot be edited |
| 3 | s[1:] |
Takes everything from index 1 onwards: 'ello' |
| 4 | s = 'J' + s[1:] |
Joins 'J' and 'ello' into a new string 'Jello' and makes s refer to it |
Operations on strings
Python allows four operations on strings: concatenation, repetition, membership and slicing. None of them changes the original string.
| Operation | Symbol | Example | Result |
|---|---|---|---|
| Concatenation (join) | + |
'Hello' + 'World!' |
'HelloWorld!' |
| Repetition | * |
'Hello' * 2 |
'HelloHello' |
| Membership | in, not in |
'Wor' in 'Hello World!' |
True |
| Slicing | [n:m:k] |
'Hello World!'[1:5] |
'ello' |
Two details cause marks to be lost. First, + needs strings on both sides: 'a' + 1 raises a TypeError, so convert first with str(1). Second, in checks for a substring (a run of characters in the same order), not just a single character. So 'Wor' in 'Hello World!' is True, but 'Wrd' in 'Hello World!' is False, because those letters are not side by side.
Slicing: str1[n:m:k]
The slice str1[n:m] returns the characters from index $n$ (included) up to index $m$ (excluded), so it has $m - n$ characters. A third number is the step: str1[n:m:k] takes every $k$-th character. Leaving a number out uses a sensible default: the start is the beginning, the stop is the end, and the step is 1. A negative step walks from right to left.
For str1 = 'Hello World!':
| Expression | Indices taken | Result | Why |
|---|---|---|---|
str1[1:5] |
1, 2, 3, 4 | 'ello' |
Stop 5 is excluded |
str1[7:10] |
7, 8, 9 | 'orl' |
3 characters, as 10 - 7 = 3 |
str1[3:20] |
3 to the end | 'lo World!' |
A stop beyond the end is cut down to the end |
str1[7:2] |
none | '' (empty) |
Start is after stop with a positive step |
str1[:5] |
0 to 4 | 'Hello' |
Missing start means 0 |
str1[6:] |
6 to 11 | 'World!' |
Missing stop means the end |
str1[0:10:2] |
0, 2, 4, 6, 8 | 'HloWr' |
Every second character |
str1[0:10:3] |
0, 3, 6, 9 | 'HlWl' |
Every third character |
str1[-6:-1] |
-6, -5, -4, -3, -2 | 'World' |
Negative indices work too |
str1[::-1] |
11 down to 0 | '!dlroW olleH' |
Step -1 reverses the string |
Unlike an index, a slice never raises an IndexError: out of range numbers are quietly trimmed.
Worked example: a longer slicing question. Let mySubject = "Computer Science" (16 characters, indices 0 to 15, the space is index 8).
| Expression | How to read it | Output |
|---|---|---|
mySubject[-7:-1] |
Indices 9 to 14 | Scienc |
mySubject[::2] |
Indices 0, 2, 4, ... 14 | Cmue cec |
mySubject[::-2] |
Indices 15, 13, 11, ... 1 | eniSrtpo |
mySubject[:3] + mySubject[3:] |
First 3 characters joined to the rest | Computer Science |
2 * mySubject |
Repetition | Computer ScienceComputer Science |
mySubject[len(mySubject) - 1] |
Index 15 | e |
A reliable method for any slice: write the indices out, cross out the excluded stop, and read the characters.
Pause and predict: s = 'PYTHON'. What are s[1:4] and s[-1:-4:-1]?
Check your answer: s[1:4] takes indices 1, 2, 3, so it is 'YTH'. In s[-1:-4:-1] the step is negative, so it walks from index -1 (N) leftwards and stops before -4 (H): N, O, H. The answer is 'NOH'.
Traversing a string
To traverse a string means to visit each character. A for loop does it directly, and a while loop does it with an index that runs from 0 to len(str1) - 1.
| Code |
|---|
str1 = 'Hello World!' |
for ch in str1: |
print(ch, end='') |
index = 0 |
while index < len(str1): |
print(str1[index], end='') |
index += 1 |
Both loops print Hello World! (once each, so the text appears twice in a row). The for version stops by itself when the characters run out. The while version stops because the condition index < len(str1) becomes false. Writing <= instead of < would raise an IndexError on the last pass.
String methods and built in functions
A method is a function that belongs to an object, called with a dot: str1.upper(). The most important fact about string methods is this: they never change the string. They return a new string (or a number, or a list), which you must store or print.
Case and size
| Method | What it returns | Example | Result |
|---|---|---|---|
len() (a function) |
Number of characters | len('Hello World!') |
12 |
title() |
First letter of every word upper, rest lower | 'hello WORLD!'.title() |
'Hello World!' |
lower() |
All letters lowercase | 'hello WORLD!'.lower() |
'hello world!' |
upper() |
All letters uppercase | 'hello WORLD!'.upper() |
'HELLO WORLD!' |
swapcase() |
Upper becomes lower and the reverse | 'Hello'.swapcase() |
'hELLO' |
Searching and counting
| Method | What it returns | Example on t = 'Hello World! Hello Hello' |
Result |
|---|---|---|---|
count(sub, start, end) |
How many times sub occurs |
t.count('Hello') |
3 |
count(sub, start, end) |
Only within the given range | t.count('Hello', 12, 25) |
2 |
find(sub, start, end) |
Index of the first occurrence, or -1 | t.find('Hello', 10, 20) |
13 |
find(sub) |
-1 if not present | t.find('Hee') |
-1 |
index(sub, start, end) |
Same as find, but raises an error if absent |
t.index('Hee') |
ValueError: substring not found |
rfind(sub) |
Index of the last occurrence | 'abcab'.rfind('ab') |
3 |
startswith(s), endswith(s) |
True or False | 'Hello World!'.endswith('!') |
True |
Testing the content (each returns True or False)
| Method | True when | True example | False example |
|---|---|---|---|
isalnum() |
Non empty and only letters and digits | 'HelloWorld2' |
'HelloWorld!!' (symbols), '' (empty) |
isalpha() |
Non empty and only letters | 'Hello' |
'Hello2' |
islower() |
Non empty, at least one letter, and every letter is lowercase | 'hello 1234' |
'1234', 'Hello World!' |
isupper() |
Non empty, at least one letter, and every letter is uppercase | 'HELLO 1234' |
'1234', 'Hello World!' |
isspace() |
Non empty and only blank, tab, newline or return characters | a string of spaces and \t |
'Hello \n' |
istitle() |
Non empty and in title case | 'Hello World!' |
'hello World!' |
Cleaning and restructuring
| Method | What it does | Example | Result |
|---|---|---|---|
lstrip(), rstrip(), strip() |
Remove spaces on the left, right, or both sides | ' hi '.strip() |
'hi' |
replace(old, new) |
Replaces all occurrences | 'Hello World!'.replace('o', '*') |
'Hell* W*rld!' |
join() |
The separator string joins the characters of its argument | '-'.join('Hello') |
'H-e-l-l-o' |
partition(sep) |
Splits at the first sep into a three part tuple |
'India is a Great Country'.partition('is') |
('India ', 'is', ' a Great Country') |
partition(sep) |
If sep is absent, the whole string and two empty strings |
'India is a Great Country'.partition('are') |
('India is a Great Country', '', '') |
split() |
A list of words, split at spaces by default | 'India is a Great Country'.split() |
['India', 'is', 'a', 'Great', 'Country'] |
split(sep) |
A list split at every sep |
'India is a Great Country'.split('a') |
['Indi', ' is ', ' Gre', 't Country'] |
find or index? Both give the position of a substring. The difference is only what happens when it is missing: find returns -1 and the program carries on, while index raises a ValueError and stops. split or partition? split returns a list of any number of pieces, and partition returns a tuple of exactly three parts, cut at the first separator only.
Worked example: methods do not change the string. What does this program print?
| Code |
|---|
s = 'hello' |
s.upper() |
print(s) |
s = s.upper() |
print(s) |
| Step | Statement | Value of s afterwards |
|---|---|---|
| 1 | s = 'hello' |
'hello' |
| 2 | s.upper() creates 'HELLO', but nothing stores it |
'hello' |
| 3 | print(s) |
prints hello |
| 4 | s = s.upper() stores the new string under the name s |
'HELLO' |
| 5 | print(s) |
prints HELLO |
Beyond the textbook. For the exam: max("Sincerity") is 'y' and min("Sincerity") is 'S', because strings are compared by character code and uppercase letters come before lowercase. In reality: the same rule decides every string comparison with < and >. Python compares two strings character by character using their Unicode code values, which you can see with ord('A'), which is 65, and ord('a'), which is 97. That is why 'Zebra' < 'apple' is True, even though Z comes late in the alphabet.
Beyond the textbook. For the exam: programs build a new string with newstr += ch inside a loop. In reality: because strings are immutable, each += makes a brand new string and copies the old one into it, which gets slow for very long text. Professional code collects the pieces in a list and joins them once at the end with ''.join(pieces). The result is the same, only faster.
Handling strings: four classic programs
The textbook turns string handling into user defined functions. Each one is a standard CUET question.
Worked example: count a character. charCount(ch, st) loops through st and adds 1 each time a character equals ch.
| Code |
|---|
def charCount(ch, st): |
count = 0 |
for character in st: |
if character == ch: |
count += 1 |
return count |
print(charCount('a', 'Today is a Holiday')) |
The string 'Today is a Holiday' has an a in Today, one alone, and one in Holiday. The count goes 0, 1, 2, 3, and the function returns 3.
Worked example: replace vowels with a star. For replaceVowel('Hello'), the function starts with an empty string newstr and examines each character.
| Code |
|---|
def replaceVowel(st): |
newstr = '' |
for character in st: |
if character in 'aeiouAEIOU': |
newstr += '*' |
else: |
newstr += character |
return newstr |
character |
In the vowel string? | newstr after the pass |
|---|---|---|
| H | No | H |
| e | Yes | H* |
| l | No | H*l |
| l | No | H*ll |
| o | Yes | H*ll* |
The function returns 'H*ll*'. Notice that the original string is untouched, because the function built a new one.
Worked example: reverse a string with negative indices. The loop for i in range(-1, -len(st) - 1, -1) visits the indices -1, -2, ... down to -len(st), and adds each character to the new string. For 'abc' the length is 3, so the range is range(-1, -4, -1), giving -1, -2, -3.
| Pass | i |
st[i] |
newstr |
|---|---|---|---|
| 1 | -1 | c | c |
| 2 | -2 | b | cb |
| 3 | -3 | a | cba |
The stop value is -len(st) - 1 and not -len(st), because the stop is excluded. Using -len(st) would leave out the first character. The one line st[::-1] gives the same result.
Worked example: palindrome check. A palindrome reads the same backwards. The function keeps two positions, i at the start and j at the end, compares the characters, and moves them towards each other. It returns False at the first mismatch and True if the pointers meet without one.
| Code |
|---|
def checkPalin(st): |
i = 0 |
j = len(st) - 1 |
while i <= j: |
if st[i] != st[j]: |
return False |
i += 1 |
j -= 1 |
return True |
| Test string | i |
j |
Compare | Decision |
|---|---|---|---|---|
'kanak' |
0 | 4 | k and k | Equal, move on |
'kanak' |
1 | 3 | a and a | Equal, move on |
'kanak' |
2 | 2 | n and n | Equal, move on |
'kanak' |
3 | 1 | i <= j is False |
Loop ends, returns True |
'computer' |
0 | 7 | c and r | Not equal, returns False at once |
The common wrong step is to forget that comparison is case sensitive. For 'Madam' the first test is 'M' != 'm', so the function returns False even though the word is a palindrome in everyday terms. To ignore case, convert first with st.lower().
Pause and predict: why does the palindrome loop need only i <= j and not a pass for every character?
Check your answer: each pass checks two characters at once, one from each end, so only half the string needs to be examined. Once the pointers cross, every pair has already matched.
Exam traps
| The trap | Why it's tempting | The truth |
|---|---|---|
s[0] = 'J' changes the string |
Lists allow this | Strings are immutable; this raises a TypeError |
s.upper() changes s |
The method name sounds like an action | It returns a new string; s stays the same unless you assign it back |
s[1:5] includes index 5 |
Counting 1 to 5 includes 5 | The stop is excluded: indices 1 to 4 |
s[7:2] raises an error |
Start is after stop | A slice never errors; it gives '' |
s[15] and s[3:20] behave alike |
Both go past the end | The index raises IndexError; the slice is trimmed |
'a' + 1 gives 'a1' |
Some languages convert silently | TypeError; use 'a' + str(1) |
find and index are identical |
Both return a position | When absent: find gives -1, index raises ValueError |
'1234'.islower() is True |
No uppercase letter | It needs at least one lowercase letter, so it is False |
60-second revision
| Topic | Lock this in |
|---|---|
| String | Sequence of Unicode characters; single, double or triple quotes; no separate char type |
| Index | 0 to $n-1$ forwards, $-n$ to $-1$ backwards; out of range is an IndexError |
| Immutable | No item assignment (TypeError); build a new string |
+ and * |
Join strings only with strings; repeat a string a whole number of times |
in |
Tests for a substring |
Slice [n:m:k] |
n included, m excluded, step k; length m minus n; never an error; [::-1] reverses |
| Traversal | for ch in s, or a while loop with index < len(s) |
| Case | lower(), upper(), title(), swapcase() |
| Search | count, find (-1), index (ValueError), startswith, endswith |
| Tests | isalnum, isalpha, islower, isupper, isspace, istitle |
| Edit | strip, lstrip, rstrip, replace, join, partition (tuple of 3), split (list) |
| Methods | Always return a new value; assign it back if you want to keep it |
Where this chapter connects
- Previous: Functions explains the user defined functions, parameters and return values used in every string program here.
- Next: Lists reuses indexing, slicing and membership, but a list is mutable, which changes what you can do.
- Also related: Flow of Control provides the
for,whileandrange()loops used to traverse strings, and Tuples and Dictionaries shows the tuple thatpartition()returns.
🎯 Practice MCQs
First 3 questions free · create a free account to unlock the rest — answers & explanations included, no payment needed
Q1. If str1 = 'Hello World!', what is the value of str1[-6:-1]?
▸ Show answer & explanation
Answer: B
Index -6 is W and -1 is the exclamation mark, which is excluded. So the slice covers W, o, r, l, d. Option A wrongly includes the last character, and option C starts one position too late.
Q2. If word = 'COMPUTER', what is word[1:6:2]?
▸ Show answer & explanation
Answer: B
The indices taken are 1, 3 and 5, because 6 is excluded. These give O, P and T. Option C would need the stop to be 7.
Q3. Which statement correctly changes s = 'Hello' into 'Jello'?
▸ Show answer & explanation
Answer: B
Strings are immutable, so a new string must be built and assigned to the name. Option A raises a TypeError, option C fails because replace needs two strings, and strings have no insert method.
🔒 12 more practice MCQs
Create a free account to unlock every MCQ in this chapter — answers and explanations included. No payment needed.
Already registered? Just log in and they'll all appear here.
Q4. Assertion (A): After the statements s = 'abc' and s.upper(), the value of s is still 'abc'. Reason (R): Strings are immutable, so string methods return a new string and do not change the original.
▸ Show answer & explanation
Answer: A
The method creates 'ABC' but the result is not stored, so s is unchanged. Immutability is exactly why methods cannot modify the original. Both statements are true and R explains A.
Q5. Match List I with List II. List I: (i) 'Hello'.find('z') (ii) 'Hello'.index('z') (iii) 'a-b'.partition('-') (iv) 'a b'.split(). List II: (p) -1 (q) ValueError (r) ('a', '-', 'b') (s) ['a', 'b'].
▸ Show answer & explanation
Answer: A
find returns -1 when the substring is absent, while index raises a ValueError. partition returns a tuple of three parts and split returns a list. Option B swaps find and index, and option C swaps partition and split.
Q6. If t = 'Hello World! Hello Hello', what is printed by print(t.find('Hello', 1), t.count('Hello'))?
▸ Show answer & explanation
Answer: B
The search for the first occurrence starts at index 1, so the Hello at index 0 is skipped and the next one is at index 13. The count method, without a range, finds all three occurrences.
Q7. What is the value of len('India is a Great Country'.split())?
▸ Show answer & explanation
Answer: B
Splitting at spaces gives the list India, is, a, Great, Country, which has 5 items. The value 24 would be the length of the string itself.
Q8. Which of the following expressions evaluate to True? (i) 'hello 1234'.islower() (ii) '1234'.islower() (iii) ''.isalnum() (iv) 'Hello World!'.istitle()
▸ Show answer & explanation
Answer: A
Expression (i) is True because there is at least one lowercase letter and no uppercase letter. Expression (ii) has no letters, so it is False. Expression (iii) is False because an empty string fails isalnum. Expression (iv) is in title case, so it is True.
Q9. What does '-'.join('Hello') return?
▸ Show answer & explanation
Answer: C
The join method places the separator between the characters of its argument. A string is a sequence of characters, so the separator goes between each pair. It does not appear at the ends.
Q10. If s = ' Hi there ' (two spaces before and two after), what is len(s.strip())?
▸ Show answer & explanation
Answer: C
The original string has 12 characters. The strip method removes the two spaces on each side, leaving Hi there, which has 8 characters. Option B is the length after removing only one side.
Q11. Assertion (A): For str1 = 'Hello World!', the expression str1[7:2] returns an empty string. Reason (R): With a positive step, a slice collects indices from the start towards the stop, and there are none when the start is beyond the stop.
▸ Show answer & explanation
Answer: A
The slice moves rightwards from index 7, which is already past the stop value 2, so no characters are collected and the result is empty. A slice never raises an IndexError, which is why A is true.
Q12. If s = 'Hello World!', which of the following expressions is True?
▸ Show answer & explanation
Answer: B
The in operator tests for a substring and is case sensitive. 'Wor' appears in the string. Option A fails because of the lowercase h, option C needs a space after World, and option D is false because lo is present.
Q13. Consider: (1) s = 'abc' (2) for i in range(-1, -4, -1): (3) print(s[i], end=''), where statement (3) is the loop body. What is printed?
▸ Show answer & explanation
Answer: B
The range gives -1, -2 and -3, so the characters are taken as c, b and a. The stop value -4 is excluded. Option C would be the result if the stop were -3.
Q14. A function compares st[i] with st[j], starting with i = 0 and j = len(st) - 1, and moves them towards each other. What does it return for the string 'Madam'?
▸ Show answer & explanation
Answer: B
String comparison is case sensitive, so the first pair M and m does not match and the function returns False at once. Converting the string to lowercase first would give True. No index is out of range.
Q15. Consider: (1) a = '5' (2) b = a + a (3) c = the string a repeated 3 times, using the repetition operator (4) print(b, c). What is printed?
▸ Show answer & explanation
Answer: B
Both operators work on strings, so no numeric addition happens. Concatenation gives 55 and repetition gives 555. No TypeError arises because there is no mixing of a string and an integer in the plus.
📊 Previous-Year Questions
Practise with real CUET Computer Science previous-year papers — every question solved, with the correct answer and a step-by-step explanation.
View solved CUET PYQ papers →Ready to drill Computer Science?
Unlock all MCQs, chapter tests, mocks & PYQs for ₹199/year.
Get UniDrill Pro