t2 --> ((1, 'two', 3), 3.25)
(t1 + t2) --> (1, 'two', 3, (1, 'two', 3), 3.25)
(t1 + t2)[3] --> (1, 'two', 3)
(t1 + t2)[2:5] --> (3, (1, 'two', 3), 3.25)
Python Fundamentals: Data Structures & Operations
Todayโs Journey: Python Data Structures & Operations
Letโs explore some essential Python concepts!
Topics covered in todayโs discussion:
Continued โฆ
map()
and filter()
functions๐ Ready to master Pythonโs most powerful data structures! ๐
Definition
Tuples are ordered collections of items that are immutable (cannot be changed after creation). Think of them as containers that are sealed shut!
Typical Use Case: Storing coordinates, RGB color values, or any data that shouldnโt change
๐๐ฆโจ
Why this works: Tuples use parentheses and are perfect when you need data that wonโt change, like a point on a map!
Example
# Like strings, tuples can be concatenated, indexed, and sliced.
t1 = (1, "two", 3)
t2 = (t1, 3.25) # Note, we include t1 here!
print(f" t2 --> {t2}")
print(f" (t1 + t2) --> {(t1 + t2)}")
print(f" (t1 + t2)[3] --> {(t1 + t2)[3]}")
print(f" (t1 + t2)[2:5] --> {(t1 + t2)[2:5]}")
t2 --> ((1, 'two', 3), 3.25)
(t1 + t2) --> (1, 'two', 3, (1, 'two', 3), 3.25)
(t1 + t2)[3] --> (1, 'two', 3)
(t1 + t2)[2:5] --> (3, (1, 'two', 3), 3.25)
Definition
Ranges generate sequences of numbers, while iterables are objects you can loop through one item at a time.
Typical Use Case: Creating number sequences for loops, generating test data, or creating patterns
๐ข๐ฏ๐
Why this works: Ranges are memory-efficient and perfect for creating predictable number sequences!
Definition
Lists are ordered, mutable collections that can store different types of data and can be modified after creation.
Typical Use Case: Storing shopping lists, student grades, or any collection that needs to grow or change
๐๐๐ง
Why this works: Lists are flexible containers that can grow, shrink, and change - perfect for dynamic data!
Definition
Cloning lists means creating independent copies so changes to one donโt affect the other.
Typical Use Case: Backing up data before modifications, creating templates, or parallel processing
๐ฏโโ๏ธ๐๐
Why this works: Proper cloning creates independent lists, preventing unwanted side effects!
Clones
L1 = [1,2,3]
print(f"L1 --> {L1}")
L2 = L1
print(f" L2 is copy of L1 --> {L2}")
L1.append("100") # Modify L1
print(f"L1 with appended value--> {L1}")
print(f" L2 is copy of L1 --> {L2}")
L2.append("2000") # Modify L2
print(f"Appending to L2 modifies L1 = {L2}")
L1 --> [1, 2, 3]
L2 is copy of L1 --> [1, 2, 3]
L1 with appended value--> [1, 2, 3, '100']
L2 is copy of L1 --> [1, 2, 3, '100']
Appending to L2 modifies L1 = [1, 2, 3, '100', '2000']
Definition
List comprehensions provide a concise way to create lists using a single line of code with optional conditions.
Typical Use Case: Transforming data, filtering lists, or creating mathematical sequences efficiently
โจ๐ญ๐ฎ
Advanced Examples
Why this works: List comprehensions are Pythonic, readable, and often faster than traditional loops!
Definition
Nested lists are lists that contain other lists as elements, creating multi-dimensional data structures.
Typical Use Case: Storing hierarchical data, representing matrices, or organizing complex information
๐ฆ๐๏ธ๐
Example
# Creating nested lists
shopping_lists = [
["apples", "bananas", "oranges"], # Fruits
["carrots", "broccoli", "spinach"], # Vegetables
["chicken", "beef", "fish"] # Proteins
]
# Accessing nested elements
print(f"First fruit: {shopping_lists[0][0]}") # apples
print(f"Second vegetable: {shopping_lists[1][1]}") # broccoli
Why this works: Nested lists let us organize related data in logical groups, like folders in a filing cabinet!
Definition
2D lists are special nested lists arranged in rows and columns, like a spreadsheet or game board.
Typical Use Case: Representing game boards, matrices, pixel data, or any grid-based information
๐ฏโก๐ฎ
Why this works: 2D lists give us row[column] access, perfect for grid-based data and games!
Definition
Higher-order operations are built-in functions like map()
, filter()
, and reduce()
that work on entire collections.
Typical Use Case: Processing large datasets, functional programming patterns, and others
๐งโก๐ ๏ธ
Example
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# map() - apply function to all elements
squares = list(map(lambda x: x**2, numbers))
print(f"Squares: {squares}")
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# filter() - keep elements that meet condition
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Evens: {evens}") # [2, 4, 6, 8, 10]
Why this works: Higher-order functions let us process entire collections efficiently with functional programming!
Function As Parameters
Add print statements to see what is happening in the code.
def apply_to_each(L,f):
""" Assume L is a list, F is a function
Mutats L by replacing each element, e, of L by f(e)"""
for i in range(len(L)):
print(f" position : {i}L[i] is now : {L[i]}")
L[i] = f(L[i])
L = [1, -2, 3.33, -5]
print(f"1. L = {L}")
print("Apply ABS() to each element of L")
apply_to_each(L,abs)
print(f"2. L = {L}")
apply_to_each(L,int)
print(f"3. L = {L}")
apply_to_each(L,float)
print(f"4. L = {L}")
print(f"Apply squaring to each element of L : {L}")
apply_to_each(L, lambda x: x**2)
print(f"5. L = {L}")
1. L = [1, -2, 3.33, -5]
Apply ABS() to each element of L
position : 0L[i] is now : 1
position : 1L[i] is now : -2
position : 2L[i] is now : 3.33
position : 3L[i] is now : -5
2. L = [1, 2, 3.33, 5]
position : 0L[i] is now : 1
position : 1L[i] is now : 2
position : 2L[i] is now : 3.33
position : 3L[i] is now : 5
3. L = [1, 2, 3, 5]
position : 0L[i] is now : 1
position : 1L[i] is now : 2
position : 2L[i] is now : 3
position : 3L[i] is now : 5
4. L = [1.0, 2.0, 3.0, 5.0]
Apply squaring to each element of L : [1.0, 2.0, 3.0, 5.0]
position : 0L[i] is now : 1.0
position : 1L[i] is now : 2.0
position : 2L[i] is now : 3.0
position : 3L[i] is now : 5.0
5. L = [1.0, 4.0, 9.0, 25.0]
The function apply_to_each()
is called higher-order because it has an argument that is itself a function.
Definition
Strings, Tuples, Ranges, and Lists are all sequence types that share common operations but have different characteristics and use cases.
Typical Use Case: Understanding when to use each type for optimal performance and code clarity
๐จโ๐ฉโ๐งโ๐ฆ๐ค๐ซ
Mutability Matters!
Why this works: All sequences share similar operations, but mutability determines which operations are allowed!
Definition
Sets are unordered collections of unique elements. No duplicates allowed!
Typical Use Case: Removing duplicates, finding common elements, or checking membership quickly
๐๐โจ
Example
# Creating sets
colors1 = {"red", "green", "blue", "red"} # Duplicate "red" ignored
colors2 = {"blue", "yellow", "purple"}
numbers = set([1, 2, 2, 3, 3, 3, 4]) # From list
print(f"Colors1: {colors1}") # {'red', 'green', 'blue'}
print(f"Numbers: {numbers}") # {1, 2, 3, 4}
colors1.add("purple")
print(f"Add purple to Colors1: {colors1}") # {'red', 'green', 'blue', 'purple'}
colors1.add("purple")
print(f"Add purple to Colors1 (again): {colors1}") # {'red', 'green', 'blue', 'purple'}
Output:
Colors1: {'blue', 'green', 'red'}
Numbers: {1, 2, 3, 4}
Add purple to Colors1: {'blue', 'green', 'purple', 'red'}
Add purple to Colors1 (again): {'blue', 'green', 'purple', 'red'}
Set Operations
# Creating sets
colors1 = {"red", "green", "blue", "red"} # Duplicate "red" ignored
colors2 = {"blue", "yellow", "purple"}
# Set operations
common = colors1 & colors2 # Intersection
all_colors = colors1 | colors2 # Union
unique_to_1 = colors1 - colors2 # Difference
print(f"Common colors: {common}") # {'blue'}
print(f"All colors: {all_colors}")
# {'red', 'green', 'blue', 'yellow', 'purple'}
# Fast membership testing
print("red" in colors1) # True - very fast!
Output:
Common colors: {'blue'}
All colors: {'blue', 'purple', 'red', 'yellow', 'green'}
True
Why this works: Sets automatically handle uniqueness and provide super-fast lookups and mathematical operations!