Chapter 2 continues the Python revision with the four built-in collection types โ strings, lists, tuples and dictionaries. It builds directly on Tour I.
Each section has a concept explanation, code example, and at least one Board Exam Tip. Don't just read โ type every code snippet yourself in Python. Typing the code yourself helps you remember the syntax.
2.1 Introduction
In Python Revision Tour I, we covered the basics โ tokens, variables, data types, and flow control. Tour II goes deeper into Python's built-in data structures โ the workhorses of real-world programming.
Strings
Ordered, immutable sequences of characters
"Hello"'Python'Lists
Ordered, mutable sequences โ can store any data type
[1"two"3.0]Tuples
Ordered, immutable sequences โ fixed after creation
(102030)Dictionaries
Key-value pairs โ fast lookups by key, not by position
{"name": "Arjun"}A typical 1-mark question: "Name the four main sequence/collection data types in Python." Answer: String, List, Tuple, Dictionary. Know which are mutable (List, Dictionary) and which are immutable (String, Tuple).
mutable means "can be changed after it is made"; immutable means "cannot be changed โ you can only make a new one".
Which one should I use? Answer these questions from the top:
graph TD
Q1{"Will you look items up\nby a NAME (key)?"}
Q2{"Is it just text?"}
Q3{"Will the items need\nto change later?"}
D["๐ Dictionary\n{'name': 'Arjun', 'age': 17}"]
S["๐ค String\n'Hello'"]
L["๐ List โ mutable\n[10, 'two', 3.0]"]
T["๐ฆ Tuple โ immutable\n(10, 20, 30)"]
Q1 -->|"Yes"| D
Q1 -->|"No, by position"| Q2
Q2 -->|"Yes"| S
Q2 -->|"No"| Q3
Q3 -->|"Yes"| L
Q3 -->|"No, fixed"| T
style D fill:#FFE0B2,stroke:#FF9800
style S fill:#C8E6C9,stroke:#4CAF50
style L fill:#BBDEFB,stroke:#2196F3
style T fill:#E1BEE7,stroke:#9C27B0
2.2 Strings in Python
A string is an ordered, immutable sequence of characters enclosed in single ' ', double " ", or triple ''' ''' quotes. Strings are one of Python's most-used data types.
# Creating strings
s1 = 'Hello'
s2 = "World"
s3 = '''This is a
multi-line string'''
print(type(s1)) # <class 'str'>
print(len(s1)) # 5
Indexing in Strings โ Both Directions!
| Index | H | e | l | l | o |
|---|---|---|---|---|---|
| Positive | 0 | 1 | 2 | 3 | 4 |
| Negative | -5 | -4 | -3 | -2 | -1 |
s = "Hello"
print(s[0]) # H (first character)
print(s[4]) # o (last character)
print(s[-1]) # o (last character, negative index)
print(s[-5]) # H (first character, negative index)
2.2.1 Item Assignment Not Supported
Strings are immutable โ once created, individual characters cannot be changed. This is one of the most important properties of strings.
What really happens: Python never edits the old string. It builds a new string and moves the name s to it.
graph LR
N(["name: s"])
OLD["'Hello'\nold string โ never changed"]
NEW["'Jello'\nbrand-new string"]
N -.->|"before"| OLD
N -->|"after s = 'J' + s[1:]"| NEW
style OLD fill:#ECEFF1,stroke:#90A4AE
style NEW fill:#C8E6C9,stroke:#4CAF50
Code Example: string_immutable.py
# string_immutable.py
s = "Hello"
# Trying to change a character โ WILL CRASH!
# s[0] = "J" โ TypeError: 'str' object does not support item assignment
# โ
CORRECT WAY โ create a NEW string
s = "J" + s[1:]
print(s) # Jello
# Another correct method
s = "Hello"
s = s.replace("H", "J")
print(s) # Jello
"Why can't we change a character of a string directly?" is a typical 1-mark question. Answer: Strings are immutable in Python. Item assignment (s[0] = 'X') raises a TypeError. To "change" a string, you must create a new string.
Beginners often try s[0] = 'X' and get confused by the TypeError. Remember: you can read from a string using index (s[0]) but you can never write to it!
2.2.2 Traversing a String
Traversal means visiting each character of a string one by one. Python gives you two clean ways to do this.
Code Example: string_traversal.py
# string_traversal.py
name = "Python"
# Method 1: for loop (PREFERRED โ clean and Pythonic)
print("Method 1: for loop")
for ch in name:
print(ch, end=" ") # P y t h o n
print()
# Method 2: while loop with index
print("Method 2: while loop")
i = 0
while i < len(name):
print(name[i], end=" ") # P y t h o n
i += 1
print()
# Method 3: for loop with range and index
print("Method 3: for with range")
for i in range(len(name)):
print(f"Index {i}: {name[i]}")
Output:
Method 1: for loop
P y t h o n
Method 2: while loop
P y t h o n
Method 3: for with range
Index 0: P
Index 1: y
Index 2: t
Index 3: h
Index 4: o
Index 5: n
Programs to count vowels/consonants, count specific characters, or check palindrome all use string traversal. Know both the for ch in s and while i < len(s) methods โ both appear in practical exams.
2.2.3 String Operators
Python supports several operators that work directly on strings.
| Operator | Name | Example | Result |
|---|---|---|---|
+ |
Concatenation | "Hello" + " World" |
"Hello World" |
* |
Repetition | "Ha" * 3 |
"HaHaHa" |
in |
Membership | "ell" in "Hello" |
True |
not in |
Non-membership | "xyz" not in "Hello" |
True |
== |
Equal | "abc" == "abc" |
True |
< > |
Comparison | "apple" < "banana" |
True (lexicographic) |
Code Example: string_operators.py
# string_operators.py
s1 = "Hello"
s2 = " World"
# Concatenation
print(s1 + s2) # Hello World
# Repetition
print("Ha" * 3) # HaHaHa
print(3 * "-") # --- (order doesn't matter!)
# Membership
print("ell" in s1) # True
print("xyz" in s1) # False
print("xyz" not in s1)# True
# Comparison (lexicographic โ based on ASCII values)
print("apple" < "banana") # True (a < b)
print("Apple" < "apple") # True (A=65 < a=97)
print("abc" == "abc") # True
Python compares strings lexicographically (like a dictionary). "Apple" < "apple" is True because uppercase letters have lower ASCII values than lowercase (A=65, a=97). This often surprises students!
2.2.4 String Slices
Slicing extracts a portion (substring) of a string. It's one of the most useful string features in Python.
Syntax: string[start : stop : step]
startโ Index to begin (inclusive). Default:0stopโ Index to end (exclusive โ NOT included). Default:len(s)stepโ Jump between characters. Default:1
Picture it: s = "PYTHON" is a row of numbered boxes. s[1:4] starts at box 1 and stops before box 4, so it takes boxes 1, 2 and 3:
graph LR
P["P\n0"] ~~~ Y["Y\n1"] ~~~ T["T\n2"] ~~~ H["H\n3"] ~~~ O["O\n4"] ~~~ N["N\n5"]
style Y fill:#C8E6C9,stroke:#4CAF50
style T fill:#C8E6C9,stroke:#4CAF50
style H fill:#C8E6C9,stroke:#4CAF50
style O fill:#FFCDD2,stroke:#F44336
๐ฉ taken by s[1:4] โ "YTH" ยท ๐ฅ box 4 is the stop โ not taken
Code Example: string_slicing.py
# string_slicing.py
s = "PYTHON"
# 0 1 2 3 4 5 (positive indices)
# -6-5-4-3-2-1 (negative indices)
# Basic slices
print(s[1:4]) # YTH (index 1,2,3 โ stop=4 is excluded)
print(s[:3]) # PYT (start defaults to 0)
print(s[3:]) # HON (stop defaults to end)
print(s[:]) # PYTHON (full string)
# Step slicing
print(s[::2]) # PTO (every 2nd character)
print(s[1::2]) # YHN (start at 1, every 2nd)
# Negative step โ REVERSAL!
print(s[::-1]) # NOHTYP (reverse the string!)
print(s[4:1:-1]) # OHT (from index 4 to 2, going backwards)
# Using negative indices
print(s[-3:]) # HON (last 3 characters)
print(s[:-3]) # PYT (everything except last 3)
s[::-1] reverses a string โ useful in both MCQs and programs. Also know: s[::2] gives alternate characters, s[1::2] gives alternate characters starting from index 1. These are typical 1-mark MCQs.
s[start:stop:step]- Begin at index
start. - Keep taking characters while the index is before
stopโ thestopindex itself is never taken. - Move by
stepeach time (a negative step walks backwards).
Quick check: with step 1, s[a:b] always gives b โ a characters โ s[1:4] gives 4 โ 1 = 3.
Backwards example: s[4:1:-1] takes indices 4, 3, 2 โ "OHT".
2.2.5 String Functions
Python's str type comes loaded with built-in functions and methods. These are exam gold!
Case Functions:
| Function | What it does | Example |
|---|---|---|
upper() |
All uppercase | "hello".upper() โ "HELLO" |
lower() |
All lowercase | "HELLO".lower() โ "hello" |
capitalize() |
First letter upper, rest lower | "hELLO".capitalize() โ "Hello" |
title() |
First letter of each word upper | "hello world".title() โ "Hello World" |
swapcase() |
Flip case of every character | "HeLLo".swapcase() โ "hEllO" |
Search & Replace:
| Function | What it does | Example |
|---|---|---|
find(sub) |
First index of sub; -1 if not found |
"Hello".find("ll") โ 2 |
index(sub) |
Like find() but raises ValueError if not found |
"Hello".index("ll") โ 2 |
count(sub) |
How many times sub appears | "Hello".count("l") โ 2 |
replace(old, new) |
Replace all occurrences | "Hello".replace("l","r") โ "Herro" |
startswith(s) |
Returns True if string starts with s | "Python".startswith("Py") โ True |
endswith(s) |
Returns True if string ends with s | "Python".endswith("on") โ True |
Split & Join:
| Function | What it does | Example |
|---|---|---|
split(sep) |
Split string into list | "a,b,c".split(",") โ ['a','b','c'] |
join(iterable) |
Join iterable with string as separator | ",".join(['a','b','c']) โ "a,b,c" |
Strip Functions:
| Function | What it does | Example |
|---|---|---|
strip() |
Remove whitespace from both ends | " Hi ".strip() โ "Hi" |
lstrip() |
Remove whitespace from left end | " Hi ".lstrip() โ "Hi " |
rstrip() |
Remove whitespace from right end | " Hi ".rstrip() โ " Hi" |
Check Functions (Return True/False):
| Function | Returns True when... |
|---|---|
isalpha() |
All characters are letters |
isdigit() |
All characters are digits |
isalnum() |
All characters are letters or digits |
isspace() |
All characters are whitespace |
isupper() |
All letters are uppercase |
islower() |
All letters are lowercase |
Other Useful Functions:
| Function | What it does | Example |
|---|---|---|
len(s) |
Length of string | len("Hello") โ 5 |
ord(ch) |
ASCII value of character | ord('A') โ 65 |
chr(n) |
Character for ASCII value | chr(65) โ 'A' |
Code Example: string_functions.py
# string_functions.py
s = " Hello, World! "
# Case operations
print(s.upper()) # " HELLO, WORLD! "
print(s.lower()) # " hello, world! "
print("hello world".title()) # "Hello World"
# Strip whitespace
print(s.strip()) # "Hello, World!"
clean = s.strip()
# Find and Count
print(clean.find("l")) # 2
print(clean.count("l")) # 3
print(clean.find("xyz"))# -1 (not found โ no crash!)
# Replace
print(clean.replace("World", "Python")) # "Hello, Python!"
# Split and Join
sentence = "apple,banana,cherry"
fruits = sentence.split(",")
print(fruits) # ['apple', 'banana', 'cherry']
print(" | ".join(fruits)) # apple | banana | cherry
# Check functions
print("Hello123".isalnum()) # True
print("Hello".isalpha()) # True
print("12345".isdigit()) # True
print(" ".isspace()) # True
# ord() and chr()
print(ord('A')) # 65
print(ord('a')) # 97
print(chr(65)) # A
print(chr(90)) # Z
Both find the position of a substring, BUT: find() returns -1 if not found (safe!), while index() raises a ValueError exception. In programs, prefer find() unless you want an error on failure. This difference can be asked as a 2-mark question.
These functions are used in encoding/decoding programs. Remember: ord('A')=65, ord('a')=97, ord('0')=48. The gap between uppercase and lowercase is always 32 (ord('a') - ord('A') == 32). This is used in Caesar cipher programs.
2.3 Lists in Python
A list is an ordered, mutable (changeable) sequence that can hold items of different data types. Lists are the most versatile data structure in Python.
"What is a list in Python?" โ Answer: A list is an ordered, mutable sequence of elements enclosed in square brackets [ ]. Elements can be of different data types. Lists support indexing, slicing, and many built-in methods.
2.3.1 Creating Lists
Code Example: creating_lists.py
# creating_lists.py
# Empty list
empty = []
print(empty) # []
# List of integers
numbers = [10, 20, 30, 40, 50]
print(numbers) # [10, 20, 30, 40, 50]
# Mixed data types โ Lists can hold anything!
mixed = [42, "Python", 3.14, True, None]
print(mixed) # [42, 'Python', 3.14, True, None]
# Nested list (list of lists)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[1][2]) # 6 (row 1, col 2)
# Using list() constructor
chars = list("CBSE")
print(chars) # ['C', 'B', 'S', 'E']
nums = list(range(1, 6))
print(nums) # [1, 2, 3, 4, 5]
Indexing in Lists โ Same as Strings:
| Index | 10 | 20 | 30 | 40 | 50 |
|---|---|---|---|---|---|
| Positive | 0 | 1 | 2 | 3 | 4 |
| Negative | -5 | -4 | -3 | -2 | -1 |
lst = [10, 20, 30, 40, 50]
print(lst[0]) # 10
print(lst[-1]) # 50
print(lst[2]) # 30
2.3.2 Lists vs. Strings
Both strings and lists are sequences, but they have key differences:
| Feature | String | List |
|---|---|---|
| Mutability | โ Immutable | โ Mutable |
| Data types stored | Characters only | Any data type |
| Syntax | "Hello" |
[1, 2, 3] |
| Item assignment | โ TypeError | โ Allowed |
Concatenation (+) |
โ | โ |
Repetition (*) |
โ | โ |
| Indexing | โ | โ |
| Slicing | โ | โ |
Code Example: list_vs_string.py
# list_vs_string.py
# String โ IMMUTABLE
s = "Hello"
# s[0] = "J" โ TypeError!
# List โ MUTABLE
lst = ["H", "e", "l", "l", "o"]
lst[0] = "J" # โ
Allowed!
print(lst) # ['J', 'e', 'l', 'l', 'o']
# Both support slicing
print(s[1:4]) # ell
print(lst[1:4]) # ['e', 'l', 'l']
# Both support membership
print("e" in s) # True
print("e" in lst) # True
"Differentiate between lists and strings in Python" โ can be asked as a 3-mark question. Always mention: mutability, data types stored, and syntax. The key difference is mutability.
2.3.3 List Operations
| Operation | Operator | Example | Result |
|---|---|---|---|
| Concatenation | + |
[1,2] + [3,4] |
[1, 2, 3, 4] |
| Repetition | * |
[0] * 3 |
[0, 0, 0] |
| Membership | in |
3 in [1,2,3] |
True |
| Non-membership | not in |
5 not in [1,2,3] |
True |
| Comparison | == |
[1,2] == [1,2] |
True |
Code Example: list_operations.py
# list_operations.py
a = [1, 2, 3]
b = [4, 5, 6]
# Concatenation โ creates a NEW list
c = a + b
print(c) # [1, 2, 3, 4, 5, 6]
# Repetition
zeros = [0] * 4
print(zeros) # [0, 0, 0, 0]
# Membership
print(2 in a) # True
print(10 in a) # False
print(10 not in a) # True
# Comparison
print([1,2,3] == [1,2,3]) # True
print([1,2,3] == [3,2,1]) # False (order matters!)
# Slicing a list โ same syntax as strings
lst = [10, 20, 30, 40, 50]
print(lst[1:4]) # [20, 30, 40]
print(lst[::-1]) # [50, 40, 30, 20, 10] (reversed!)
print(lst[::2]) # [10, 30, 50]
2.3.4 List Manipulation
Since lists are mutable, we can directly modify their elements โ unlike strings!
Code Example: list_manipulation.py
# list_manipulation.py
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
# โ Updating an element
fruits[1] = "blueberry"
print(fruits) # ['apple', 'blueberry', 'cherry', 'date', 'elderberry']
# โก Updating a slice (replace multiple elements at once)
fruits[2:4] = ["coconut", "durian"]
print(fruits) # ['apple', 'blueberry', 'coconut', 'durian', 'elderberry']
# โข Deleting an element by index
del fruits[0]
print(fruits) # ['blueberry', 'coconut', 'durian', 'elderberry']
# โฃ Deleting a slice
del fruits[1:3]
print(fruits) # ['blueberry', 'elderberry']
# โค Inserting in the middle via slice assignment
fruits[1:1] = ["fig", "grape"]
print(fruits) # ['blueberry', 'fig', 'grape', 'elderberry']
fruits[1:3] = ["mango"] does NOT just change elements 1 and 2 โ it replaces that slice with the new list. The length of the list may change! Slice assignment is powerful but needs care.
2.3.5 Making True Copy of a List
This is one of the trickiest topics for students โ and important for exams.
The Problem: Shallow (Alias) Copy
When you write b = a, Python does NOT create a new list. Both a and b point to the same list object. Changing one changes both!
a variable is just a name tag stuck on a box of data. b = a sticks a second tag on the same box โ so a change made through either name shows up in both. b = a[:] builds a second box with the same contents, so the two lists are independent.
graph LR
subgraph CP["โ
copy1 = original[:]"]
O2(["original"])
C2(["copy1"])
L2["[1, 2, 3, 4, 5]"]
L3["[999, 2, 3, 4, 5]\na SEPARATE list"]
O2 --> L2
C2 --> L3
end
subgraph AL["โ alias = original"]
O1(["original"])
A1(["alias"])
L1["[999, 2, 3, 4, 5]\nONE list in memory"]
O1 --> L1
A1 --> L1
end
style L1 fill:#FFCDD2,stroke:#F44336
style L2 fill:#C8E6C9,stroke:#4CAF50
style L3 fill:#C8E6C9,stroke:#4CAF50
Code Example: list_copy.py
# list_copy.py
# โ ALIAS (NOT a copy!)
original = [1, 2, 3, 4, 5]
alias = original # Both point to SAME list!
alias[0] = 999
print(original) # [999, 2, 3, 4, 5] โ Original CHANGED!
print(alias) # [999, 2, 3, 4, 5]
# โ
TRUE COPY โ Method 1: Slicing
original = [1, 2, 3, 4, 5]
copy1 = original[:]
copy1[0] = 999
print(original) # [1, 2, 3, 4, 5] โ Original SAFE!
print(copy1) # [999, 2, 3, 4, 5]
# โ
TRUE COPY โ Method 2: list() constructor
copy2 = list(original)
copy2[0] = 888
print(original) # [1, 2, 3, 4, 5] โ Original SAFE!
# โ
TRUE COPY โ Method 3: .copy() method
copy3 = original.copy()
copy3[0] = 777
print(original) # [1, 2, 3, 4, 5] โ Original SAFE!
"What is the difference between b = a and b = a[:] for lists?" โ This is a 3-mark question. Always explain:
b = acreates an alias (both refer to same object)b = a[:]creates a true copy (independent objects) Useid()to prove they are different:id(original) != id(copy1).
a = [1, 2, 3]
b = a # alias
c = a[:] # true copy
print(id(a) == id(b)) # True โ same object!
print(id(a) == id(c)) # False โ different objects!2.3.6 List Functions
Python provides a rich set of list functions and methods. Know them all!
Methods that MODIFY the list (in-place):
| Method | What it does | Example |
|---|---|---|
append(x) |
Add x at the END | lst.append(6) |
extend(iterable) |
Add all items of iterable at end | lst.extend([7,8]) |
insert(i, x) |
Insert x at index i | lst.insert(2, 99) |
remove(x) |
Remove first occurrence of x | lst.remove(99) |
pop(i) |
Remove & return item at index i (default: last) | lst.pop() |
sort() |
Sort list in place (ascending) | lst.sort() |
sort(reverse=True) |
Sort in descending order | lst.sort(reverse=True) |
reverse() |
Reverse the list in place | lst.reverse() |
clear() |
Remove all elements | lst.clear() |
Methods/Functions that return a value (do NOT modify):
| Function | What it does | Example |
|---|---|---|
len(lst) |
Number of elements | len([1,2,3]) โ 3 |
count(x) |
Count occurrences of x | [1,2,2,3].count(2) โ 2 |
index(x) |
Index of first occurrence of x | [1,2,3].index(2) โ 1 |
min(lst) |
Minimum element | min([5,1,3]) โ 1 |
max(lst) |
Maximum element | max([5,1,3]) โ 5 |
sum(lst) |
Sum of all elements | sum([1,2,3]) โ 6 |
sorted(lst) |
Returns new sorted list | sorted([3,1,2]) โ [1,2,3] |
Code Example: list_functions.py
# list_functions.py
lst = [30, 10, 50, 20, 40]
print("Original:", lst) # [30, 10, 50, 20, 40]
# append โ adds ONE element to end
lst.append(60)
print("After append(60):", lst) # [30, 10, 50, 20, 40, 60]
# extend โ adds MULTIPLE elements to end
lst.extend([70, 80])
print("After extend:", lst) # [30, 10, 50, 20, 40, 60, 70, 80]
# insert โ at specific position
lst.insert(1, 5)
print("After insert(1,5):", lst) # [30, 5, 10, 50, 20, 40, 60, 70, 80]
# remove โ first occurrence
lst.remove(5)
print("After remove(5):", lst) # [30, 10, 50, 20, 40, 60, 70, 80]
# pop โ removes and returns last element
popped = lst.pop()
print("Popped:", popped) # 80
print("After pop():", lst) # [30, 10, 50, 20, 40, 60, 70]
# pop(index) โ removes and returns specific index
popped_at = lst.pop(2)
print("Popped at [2]:", popped_at) # 50
# sort and reverse
lst.sort()
print("After sort():", lst) # [10, 20, 30, 40, 60, 70]
lst.reverse()
print("After reverse():", lst) # [70, 60, 40, 30, 20, 10]
# Statistics
print("min:", min(lst)) # 10
print("max:", max(lst)) # 70
print("sum:", sum(lst)) # 230
print("len:", len(lst)) # 6
Revise this difference carefully.
append([4,5])โ adds the list as a single element:[1,2,3,[4,5]]extend([4,5])โ adds each item separately:[1,2,3,4,5]Afterlst.append([4,5]),len(lst)increases by 1. Afterlst.extend([4,5]), it increases by 2.
graph LR
A["lst = [1, 2, 3]"]
B["[1, 2, 3, [4, 5]]\nlength 4 โ the whole list\ngoes in as ONE item"]
C["[1, 2, 3, 4, 5]\nlength 5 โ each item\ngoes in separately"]
A -->|"lst.append([4, 5])"| B
A -->|"lst.extend([4, 5])"| C
style B fill:#FFE0B2,stroke:#FF9800
style C fill:#C8E6C9,stroke:#4CAF50
lst.sort()โ modifies the original list, returnsNonesorted(lst)โ does NOT modify original list, returns a new sorted list Writinglst = lst.sort()is a classic bug โlstbecomesNone!
2.4 Tuples in Python
A tuple is an ordered, immutable sequence of elements. Think of a tuple as a "read-only list" โ once created, it cannot be changed.
"When would you use a tuple instead of a list?" โ Answer: When data should not change (e.g., days of week, RGB color values, database records), use a tuple. Tuples are also faster than lists and can be used as dictionary keys (lists cannot).
2.4.1 Creating Tuples
Code Example: creating_tuples.py
# creating_tuples.py
# Empty tuple
t0 = ()
print(t0) # ()
# Tuple of integers
t1 = (10, 20, 30)
print(t1) # (10, 20, 30)
# Mixed data types
t2 = (42, "Python", 3.14, True)
print(t2)
# โ ๏ธ CRITICAL: Single-element tuple โ the comma is MANDATORY!
single_wrong = (42) # This is just an int, NOT a tuple!
single_right = (42,) # This IS a tuple โ note the comma!
print(type(single_wrong)) # <class 'int'>
print(type(single_right)) # <class 'tuple'>
# Tuple without parentheses (tuple packing)
t3 = 1, 2, 3
print(t3) # (1, 2, 3)
print(type(t3)) # <class 'tuple'>
# Using tuple() constructor
t4 = tuple([1, 2, 3]) # from list
t5 = tuple("CBSE") # from string
print(t4) # (1, 2, 3)
print(t5) # ('C', 'B', 'S', 'E')
t = (42) is an integer, NOT a tuple! Python reads the parentheses as just grouping.
t = (42,) is a tuple โ the trailing comma makes all the difference.
This is a typical 1-mark MCQ trap. Remember: ONE item โ must have a comma!
2.4.2 Tuples vs. Lists
| Feature | Tuple | List |
|---|---|---|
| Mutability | โ Immutable | โ Mutable |
| Syntax | ( ) |
[ ] |
| Speed | โ Faster | Slower |
| Memory | Less | More |
| Can be dict key | โ Yes | โ No |
| Has all list methods | โ No | โ Yes |
| Use case | Fixed data | Dynamic data |
Code Example: tuple_vs_list.py
# tuple_vs_list.py
t = (1, 2, 3)
lst = [1, 2, 3]
# โ Tuple item assignment raises TypeError
# t[0] = 99 โ TypeError: 'tuple' object does not support item assignment
# โ
List item assignment works
lst[0] = 99
print(lst) # [99, 2, 3]
# Tuples can be dict keys, lists CANNOT
my_dict = {(1, 2): "point A", (3, 4): "point B"}
print(my_dict[(1, 2)]) # point A
# This would CRASH:
# bad_dict = {[1,2]: "point"} โ TypeError: unhashable type: 'list'
2.4.3 Tuple Operations
Tuples support all read-only operations that lists do:
| Operation | Example | Result |
|---|---|---|
| Concatenation | (1,2) + (3,4) |
(1, 2, 3, 4) |
| Repetition | (0,) * 3 |
(0, 0, 0) |
| Membership | 2 in (1,2,3) |
True |
| Indexing | t[1] |
Second element |
| Slicing | t[1:3] |
Sub-tuple |
| Comparison | (1,2) == (1,2) |
True |
Code Example: tuple_operations.py
# tuple_operations.py
t1 = (10, 20, 30)
t2 = (40, 50, 60)
# Concatenation โ creates NEW tuple
t3 = t1 + t2
print(t3) # (10, 20, 30, 40, 50, 60)
# Repetition
t4 = (0,) * 4
print(t4) # (0, 0, 0, 0)
# Membership
print(20 in t1) # True
print(99 in t1) # False
# Indexing
print(t1[0]) # 10
print(t1[-1]) # 30
# Slicing
print(t3[2:5]) # (30, 40, 50)
print(t3[::-1]) # (60, 50, 40, 30, 20, 10)
# Tuple unpacking โ very Pythonic!
a, b, c = t1
print(a, b, c) # 10 20 30
is a clean Python technique โ assign a tuple's values to multiple variables in one line: x, y, z = (1, 2, 3). This works because Python's multiple assignment is based on tuple packing/unpacking.
2.4.4 Tuple Functions and Methods
Tuples have only 2 methods (because they're immutable โ can't add, remove, or sort):
| Method | What it does | Example |
|---|---|---|
count(x) |
Count occurrences of x | (1,2,2,3).count(2) โ 2 |
index(x) |
Index of first occurrence of x | (10,20,30).index(20) โ 1 |
Built-in functions that work on tuples:
| Function | What it does |
|---|---|
len(t) |
Number of elements |
min(t) |
Minimum element |
max(t) |
Maximum element |
sum(t) |
Sum of all numeric elements |
sorted(t) |
Returns a new sorted list (not tuple!) |
tuple(seq) |
Convert sequence to tuple |
Code Example: tuple_functions.py
# tuple_functions.py
marks = (85, 92, 78, 92, 88, 70)
print("Length:", len(marks)) # 6
print("Maximum:", max(marks)) # 92
print("Minimum:", min(marks)) # 70
print("Sum:", sum(marks)) # 505
print("Count of 92:", marks.count(92)) # 2 โ count() is a tuple method
print("Index of 78:", marks.index(78)) # 2
# sorted() returns a LIST, not a tuple!
sorted_marks = sorted(marks)
print("Sorted:", sorted_marks) # [70, 78, 85, 88, 92, 92]
print("Type:", type(sorted_marks)) # <class 'list'>
# If you need a sorted tuple:
sorted_tuple = tuple(sorted(marks))
print("Sorted tuple:", sorted_tuple) # (70, 78, 85, 88, 92, 92)
sorted(t) on a tuple returns a list, not a tuple! Remember this for MCQs. Also: tuples have only count() and index() methods โ they do NOT have append(), remove(), sort(), etc.
2.5 Dictionaries in Python
A dictionary is a collection of key-value pairs. It's like a real dictionary โ you look up a word (key) to find its definition (value). You find an item by its key, never by a position number. (Since Python 3.7, a dictionary also remembers the order in which you added the items.)
graph LR
K1["๐ 'name'"] --> V1["'Arjun'"]
K2["๐ 'age'"] --> V2["17"]
K3["๐ 'marks'"] --> V3["95"]
style K1 fill:#FFE0B2,stroke:#FF9800
style K2 fill:#FFE0B2,stroke:#FF9800
style K3 fill:#FFE0B2,stroke:#FF9800
student["age"] follows the arrow from the key 'age' and gives 17.
"Define dictionary in Python" โ Answer: A dictionary is a mutable collection of key-value pairs enclosed in curly braces { }, where items are found by key, not by position (from Python 3.7 it also keeps insertion order). Each key must be unique and immutable (string, number, or tuple). Values can be of any type.
2.5.1 Creating a Dictionary
Code Example: creating_dict.py
# creating_dict.py
# Empty dictionary
d0 = {}
print(d0) # {}
print(type(d0)) # <class 'dict'>
# Dictionary with initial values
student = {
"name": "Arjun",
"age": 17,
"marks": 95,
"passed": True
}
print(student)
# Mixed key types (keys must be immutable)
mixed = {
1: "one",
"two": 2,
(3, 4): "tuple key"
}
print(mixed[1]) # one
print(mixed["two"]) # 2
print(mixed[(3, 4)]) # tuple key
# Using dict() constructor
d2 = dict(name="Priya", age=16, city="Delhi")
print(d2) # {'name': 'Priya', 'age': 16, 'city': 'Delhi'}
# Creating from list of tuples
d3 = dict([("a", 1), ("b", 2), ("c", 3)])
print(d3) # {'a': 1, 'b': 2, 'c': 3}
2.5.2 Accessing Elements of a Dictionary
Code Example: accessing_dict.py
# accessing_dict.py
student = {"name": "Arjun", "age": 17, "marks": 95}
# Method 1: Direct key access โ FAST
print(student["name"]) # Arjun
print(student["marks"]) # 95
# โ Key error if key doesn't exist
# print(student["grade"]) โ KeyError: 'grade'
# โ
Method 2: get() โ SAFE (returns None or default if key not found)
print(student.get("grade")) # None (no crash!)
print(student.get("grade", "N/A")) # N/A (custom default value)
# Checking if key exists
if "age" in student:
print("Age found:", student["age"]) # Age found: 17
d["key"]โ raises KeyError if key doesn't existd.get("key")โ returns None (or custom default) if key doesn't exist In safe programs, always useget(). This difference is tested in both MCQ and programming questions!
2.5.3 Characteristics of a Dictionary
Unique Keys
Each key appears EXACTLY once โ duplicate keys overwrite
{"a":1"a":2} โ {"a":2}Immutable Keys
Keys must be immutable: strings, numbers, tuples โ NOT lists
Use "name" not ["name"]Mutable Values
Values can be any data type and can be changed
Values can even be lists or dicts!Ordered (3.7+)
From Python 3.7, dicts maintain insertion order
Items appear in order you added themCode Example: dict_characteristics.py
# dict_characteristics.py
# Duplicate keys โ LAST VALUE WINS
d = {"a": 1, "b": 2, "a": 99}
print(d) # {'a': 99, 'b': 2} โ first "a":1 is overwritten!
# Values can be any type โ even lists, dicts!
complex_dict = {
"name": "Arjun",
"scores": [85, 90, 88],
"address": {"city": "Delhi", "pin": 110001}
}
print(complex_dict["scores"]) # [85, 90, 88]
print(complex_dict["address"]["city"]) # Delhi
# Lists CANNOT be keys (unhashable!)
# bad = {[1,2]: "value"} โ TypeError: unhashable type: 'list'
# Tuples CAN be keys
good = {(1, 2): "coordinates"}
print(good[(1, 2)]) # coordinates
2.5.4 Dictionary Operations
Code Example: dict_operations.py
# dict_operations.py
student = {"name": "Arjun", "age": 17}
# โ Adding a new key-value pair
student["marks"] = 95
print(student) # {'name': 'Arjun', 'age': 17, 'marks': 95}
# โก Updating an existing value
student["age"] = 18
print(student) # {'name': 'Arjun', 'age': 18, 'marks': 95}
# โข Deleting a key-value pair using del
del student["age"]
print(student) # {'name': 'Arjun', 'marks': 95}
# โฃ Deleting using pop() โ also returns the value
removed = student.pop("marks")
print("Removed value:", removed) # 95
print(student) # {'name': 'Arjun'}
# โค Checking membership (checks KEYS only!)
student = {"name": "Arjun", "age": 17, "marks": 95}
print("name" in student) # True
print("Arjun" in student) # False โ "Arjun" is a VALUE, not a KEY!
print(17 in student) # False โ 17 is a VALUE!
# โฅ Traversal
for key in student:
print(key, ":", student[key])
"key" in d checks only keys, NOT values! "Arjun" in student is False even though "Arjun" is in the dictionary (as a value). Use "Arjun" in student.values() to search values.
2.5.5 Dictionary Functions and Methods
| Method | What it does | Returns |
|---|---|---|
keys() |
All keys | dict_keys view |
values() |
All values | dict_values view |
items() |
All key-value pairs as tuples | dict_items view |
get(key, default) |
Value for key; default if not found | Value or default |
update(d2) |
Add/update with another dict | None (in-place) |
pop(key) |
Remove and return value for key | Value |
popitem() |
Remove and return last inserted pair | (key, value) tuple |
clear() |
Remove all items | None |
copy() |
Shallow copy | New dict |
setdefault(key, val) |
Return value; set default if key absent | Value |
fromkeys(seq, val) |
Create dict with keys from seq | New dict |
len(d) |
Number of key-value pairs | Integer |
Code Example: dict_methods.py
# dict_methods.py
student = {"name": "Arjun", "age": 17, "marks": 95, "grade": "A"}
# keys(), values(), items()
print(student.keys()) # dict_keys(['name', 'age', 'marks', 'grade'])
print(student.values()) # dict_values(['Arjun', 17, 95, 'A'])
print(student.items()) # dict_items([('name', 'Arjun'), ...])
# Convert to lists if needed
key_list = list(student.keys())
print(key_list) # ['name', 'age', 'marks', 'grade']
# update() โ adds/modifies multiple items at once
student.update({"city": "Delhi", "age": 18})
print(student) # age updated to 18, city added
# Traversal using items()
print("\nStudent Report:")
for key, value in student.items():
print(f" {key:10}: {value}")
# fromkeys() โ create dict with fixed value
subjects = ["Physics", "Chemistry", "Maths", "CS"]
marks_template = dict.fromkeys(subjects, 0)
print(marks_template)
# {'Physics': 0, 'Chemistry': 0, 'Maths': 0, 'CS': 0}
# popitem() โ removes last inserted item
last = student.popitem()
print("Removed:", last) # ('city', 'Delhi') โ the last INSERTED key (updating 'age' did not move it)
# Nested dict traversal
school = {
"class12": {"strength": 40, "subjects": 5},
"class11": {"strength": 35, "subjects": 5}
}
for cls, info in school.items():
print(f"{cls}: {info['strength']} students")
Know these three methods well, including what each one returns: view objects, not lists. Convert to list with list(d.keys()) if you need a list. Also know that traversal using for k, v in d.items(): is the most Pythonic way to iterate over a dict.
โ ๏ธ Common Errors and Misconceptions
| Mistake | Why it goes wrong | Correct way |
|---|---|---|
โ s[0] = "J" to change a string |
Strings are immutable โ TypeError |
โ
s = "J" + s[1:] |
โ Expecting s[1:4] to include index 4 |
The stop index is never included | โ
s[1:4] gives indices 1, 2, 3 |
โ s.index("x") when "x" may be missing |
index() raises ValueError |
โ
s.find("x") returns -1 |
โ b = a to copy a list |
Makes an alias โ one list, two names | โ
b = a[:], a.copy() or list(a) |
โ lst.append([4, 5]) to add two items |
Adds ONE item: a nested list | โ
lst.extend([4, 5]) |
โ lst = lst.sort() |
sort() returns None |
โ
lst.sort() or lst = sorted(lst) |
โ t = (42) for a one-item tuple |
That is just the integer 42 | โ
t = (42,) |
โ "Arjun" in student to search values |
in checks only the KEYS |
โ
"Arjun" in student.values() |
โ d["grade"] when the key may be missing |
Raises KeyError |
โ
d.get("grade", "N/A") |
The 4 collection types
| Type | Brackets | Ordered? | Mutable? | Example |
|---|---|---|---|---|
| String | " " or ' ' |
โ by index | โ No | "PYTHON" |
| List | [ ] |
โ by index | โ Yes | [1, "two", 3.0] |
| Tuple | ( ) |
โ by index | โ No | (10, 20, 30) |
| Dictionary | { } |
by key (insertion order kept, 3.7+) | โ Yes | {"name": "Arjun"} |
Indexing and Slicing (strings, lists and tuples), with s = "PYTHON"
- Forward index
0tolen-1, backward index-1(last) to-len s[start:stop:step]โ the stop index is never included; a slice never gives anIndexError
| Expression | Result |
|---|---|
s[1:4] |
'YTH' |
s[:3] |
'PYT' |
s[-2:] |
'ON' |
s[::2] |
'PTO' |
s[::-1] |
'NOHTYP' |
s[2:100] |
'THON' (no error) |
Strings
- Immutable โ
s[0] = "J"givesTypeError; build a new string:s = "J" + s[1:] +joins,*repeats ("Ha" * 3โ'HaHaHa'),inchecks a substringfind()โ-1if not found |index()โValueErrorif not foundsplit()โ string to list |"-".join(lst)โ list to stringcapitalize()โ only the first letter upper |title()โ first letter of every word upperisalpha(),isdigit(),isalnum(),isspace(),isupper(),islower()โ returnTrue/Falseord("A")โ65|chr(97)โ'a'
Lists
- Mutable โ
lst[0] = 99works append(x)โ adds one item (even a whole list) |extend([x, y])โ adds each item |insert(i, x)โ at indexiremove(x)โ by value |pop()โ removes and returns the last |pop(i)โ by index |del lst[i]lst.sort()changes the list and returnsNone|sorted(lst)returns a new listb = aโ alias (one list, two names) | True copy:b = a[:],a.copy()orlist(a)
Tuples
- Immutable, only 2 methods:
count()andindex() - One-item tuple needs a comma:
(42,)is a tuple,(42)is just the integer 42 sorted(t)returns a list | a tuple can be a dictionary key, a list cannot
Dictionaries
- Keys must be unique and immutable (string, number, tuple); a repeated key keeps the last value
d[key]โKeyErrorif missing |d.get(key)โNone(or a default) if missingd[key] = valueโ adds a new pair or changes an existing onekeys(),values(),items()|update(d2)merges |pop(key)removes |popitem()removes the last added pairinchecks keys only: usex in d.values()to search values
Quick check: predict the output
a = [1, 2, 3]
b = a
c = a[:]
a.append(4)
print(b, c)
d = {"x": 1, "y": 2}
d["x"] = 10
print(len(d), d.get("z", 0), "y" in d)
Output:
[1, 2, 3, 4] [1, 2, 3]
2 0 True
b is an alias, so it shows the appended 4; c is a true copy. d["x"] = 10 changes a value, it does not add a key, so len(d) is still 2.
Practice Problems
Test your mastery of Tour II topics with these board-style problems:
Strings:
- Write a program to count the number of vowels and consonants in a string
- Write a program to check if a string is a palindrome (without using slicing)
- Write a program to reverse words in a sentence:
"Hello World"โ"World Hello" - Write a program to count occurrences of each character in a string using a dictionary
- Write a program using
ord()to encode a message (shift each letter by 3 โ Caesar cipher)
Lists & Tuples:
- Write a program to find the second-largest element in a list (without sorting)
- Write a program to remove duplicate elements from a list while preserving order
- Write a program to find the frequency of each element in a list using a dictionary
- Demonstrate the difference between alias copy and true copy of a list
- Write a program to flatten a list of lists:
[[1,2],[3,4],[5,6]]โ[1,2,3,4,5,6]
Dictionaries:
- Write a program to merge two dictionaries
- Write a program to create a frequency dictionary from a list of words
- Write a program to find the key with the maximum value in a dictionary
- Write a program to invert a dictionary (swap keys and values)
Quick Reference
Strings โ Key Facts:
- Immutable โ
s[0] = 'X'raises TypeError - Slicing:
s[start:stop:step], reverse:s[::-1] find()โ-1if not found |index()โ ValueError if not foundsplit()โ string to list |join()โ list to stringord()โ ASCII value |chr()โ character from ASCII
Lists โ Key Facts:
- Mutable โ
lst[0] = 99is allowed - True copy:
lst[:]orlst.copy()orlist(lst)โ NOTb = a append(x)โ adds 1 item |extend([x,y])โ adds multiplesort()โ modifies original |sorted()โ returns new listpop()โ removes last |pop(i)โ removes at index i
Tuples โ Key Facts:
- Immutable โ cannot change after creation
- Single-element:
(42,)โ comma is mandatory! - Only 2 methods:
count()andindex() sorted(t)returns a list, not a tuple- Can be used as dictionary keys (lists cannot)
Dictionaries โ Key Facts:
- Key-value pairs:
{key: value} - Keys must be unique and immutable
d[key]โ KeyError if missing |d.get(key)โ None if missinginchecks keys, NOT valueskeys(),values(),items()โ view objects