mirror of
https://github.com/blshaer/python-by-example.git
synced 2026-03-27 23:29:25 +01:00
287 lines
8.5 KiB
Python
287 lines
8.5 KiB
Python
"""
|
|
================================================================================
|
|
File: 02_tuples.py
|
|
Topic: Python Tuples - Ordered, Immutable Collections
|
|
================================================================================
|
|
|
|
This file demonstrates Python tuples, which are ordered, immutable collections.
|
|
Unlike lists, tuples cannot be modified after creation, making them useful
|
|
for data that should not change.
|
|
|
|
Key Concepts:
|
|
- Creating tuples
|
|
- Tuple immutability
|
|
- Accessing elements
|
|
- Tuple unpacking
|
|
- When to use tuples vs lists
|
|
- Named tuples for clarity
|
|
|
|
================================================================================
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 1. Creating Tuples
|
|
# -----------------------------------------------------------------------------
|
|
# Tuples are created with parentheses () or just commas
|
|
|
|
print("--- Creating Tuples ---")
|
|
|
|
# Empty tuple
|
|
empty_tuple = ()
|
|
print(f"Empty tuple: {empty_tuple}")
|
|
|
|
# Tuple with elements
|
|
coordinates = (10, 20)
|
|
colors = ("red", "green", "blue")
|
|
mixed = (1, "hello", 3.14, True)
|
|
|
|
print(f"Coordinates: {coordinates}")
|
|
print(f"Colors: {colors}")
|
|
print(f"Mixed types: {mixed}")
|
|
|
|
# Single element tuple - needs trailing comma!
|
|
single = (42,) # This is a tuple
|
|
not_tuple = (42) # This is just an integer!
|
|
print(f"\nSingle element tuple: {single}, type: {type(single)}")
|
|
print(f"Without comma: {not_tuple}, type: {type(not_tuple)}")
|
|
|
|
# Tuple without parentheses (packing)
|
|
packed = 1, 2, 3
|
|
print(f"Packed tuple: {packed}, type: {type(packed)}")
|
|
|
|
# Using tuple() constructor
|
|
from_list = tuple([1, 2, 3])
|
|
from_string = tuple("Python")
|
|
print(f"From list: {from_list}")
|
|
print(f"From string: {from_string}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 2. Accessing Elements
|
|
# -----------------------------------------------------------------------------
|
|
# Same indexing and slicing as lists
|
|
|
|
print("\n--- Accessing Elements ---")
|
|
|
|
fruits = ("apple", "banana", "cherry", "date", "elderberry")
|
|
print(f"Tuple: {fruits}")
|
|
|
|
print(f"First element: {fruits[0]}")
|
|
print(f"Last element: {fruits[-1]}")
|
|
print(f"Slice [1:4]: {fruits[1:4]}")
|
|
print(f"Every other: {fruits[::2]}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 3. Tuple Immutability
|
|
# -----------------------------------------------------------------------------
|
|
# Tuples cannot be modified after creation
|
|
|
|
print("\n--- Immutability ---")
|
|
|
|
point = (10, 20, 30)
|
|
print(f"Original point: {point}")
|
|
|
|
# This would raise an error:
|
|
# point[0] = 100 # TypeError: 'tuple' object does not support item assignment
|
|
|
|
# However, you can create a new tuple
|
|
point = (100,) + point[1:] # Create new tuple
|
|
print(f"New point: {point}")
|
|
|
|
# Note: Mutable objects inside tuples can still be modified
|
|
mutable_inside = ([1, 2], [3, 4])
|
|
mutable_inside[0].append(3) # This works!
|
|
print(f"Mutable inside tuple: {mutable_inside}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4. Tuple Unpacking
|
|
# -----------------------------------------------------------------------------
|
|
# Assign tuple elements to multiple variables
|
|
|
|
print("\n--- Tuple Unpacking ---")
|
|
|
|
# Basic unpacking
|
|
coordinates = (100, 200, 300)
|
|
x, y, z = coordinates
|
|
print(f"Unpacked: x={x}, y={y}, z={z}")
|
|
|
|
# Swap values using tuple unpacking
|
|
a, b = 1, 2
|
|
print(f"Before swap: a={a}, b={b}")
|
|
a, b = b, a # Elegant swap!
|
|
print(f"After swap: a={a}, b={b}")
|
|
|
|
# Unpacking with * (extended unpacking)
|
|
numbers = (1, 2, 3, 4, 5)
|
|
first, *middle, last = numbers
|
|
print(f"\nExtended unpacking:")
|
|
print(f" first: {first}")
|
|
print(f" middle: {middle}") # This becomes a list!
|
|
print(f" last: {last}")
|
|
|
|
# Ignoring values with underscore
|
|
data = ("John", 25, "Developer", "NYC")
|
|
name, _, job, _ = data # Ignore age and city
|
|
print(f"Name: {name}, Job: {job}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 5. Tuple Methods
|
|
# -----------------------------------------------------------------------------
|
|
# Tuples have only two methods (because they're immutable)
|
|
|
|
print("\n--- Tuple Methods ---")
|
|
|
|
numbers = (1, 2, 3, 2, 4, 2, 5)
|
|
print(f"Tuple: {numbers}")
|
|
|
|
# count() - Count occurrences
|
|
print(f"Count of 2: {numbers.count(2)}")
|
|
|
|
# index() - Find index of first occurrence
|
|
print(f"Index of 4: {numbers.index(4)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 6. Tuple Operations
|
|
# -----------------------------------------------------------------------------
|
|
|
|
print("\n--- Tuple Operations ---")
|
|
|
|
# Concatenation
|
|
t1 = (1, 2, 3)
|
|
t2 = (4, 5, 6)
|
|
combined = t1 + t2
|
|
print(f"{t1} + {t2} = {combined}")
|
|
|
|
# Repetition
|
|
repeated = (0,) * 5
|
|
print(f"(0,) * 5 = {repeated}")
|
|
|
|
# Membership
|
|
colors = ("red", "green", "blue")
|
|
print(f"'green' in colors: {'green' in colors}")
|
|
|
|
# Length
|
|
print(f"len(colors): {len(colors)}")
|
|
|
|
# Min, Max, Sum
|
|
nums = (5, 2, 8, 1, 9)
|
|
print(f"min: {min(nums)}, max: {max(nums)}, sum: {sum(nums)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 7. Tuples as Dictionary Keys
|
|
# -----------------------------------------------------------------------------
|
|
# Tuples can be used as dictionary keys (lists cannot)
|
|
|
|
print("\n--- Tuples as Dictionary Keys ---")
|
|
|
|
# Mapping (x, y) coordinates to location names
|
|
locations = {
|
|
(40.7128, -74.0060): "New York City",
|
|
(34.0522, -118.2437): "Los Angeles",
|
|
(51.5074, -0.1278): "London"
|
|
}
|
|
|
|
print("Locations dictionary:")
|
|
for coords, city in locations.items():
|
|
print(f" {coords}: {city}")
|
|
|
|
# Access by tuple key
|
|
coord = (40.7128, -74.0060)
|
|
print(f"\nLocation at {coord}: {locations[coord]}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 8. Returning Multiple Values from Functions
|
|
# -----------------------------------------------------------------------------
|
|
# Functions can return tuples
|
|
|
|
print("\n--- Functions Returning Tuples ---")
|
|
|
|
def get_stats(numbers):
|
|
"""Return multiple statistics as a tuple."""
|
|
return min(numbers), max(numbers), sum(numbers) / len(numbers)
|
|
|
|
data = [10, 20, 30, 40, 50]
|
|
minimum, maximum, average = get_stats(data)
|
|
print(f"Stats for {data}:")
|
|
print(f" Min: {minimum}, Max: {maximum}, Avg: {average}")
|
|
|
|
def divide_with_remainder(a, b):
|
|
"""Return quotient and remainder."""
|
|
return a // b, a % b
|
|
|
|
quotient, remainder = divide_with_remainder(17, 5)
|
|
print(f"\n17 ÷ 5 = {quotient} remainder {remainder}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 9. Named Tuples
|
|
# -----------------------------------------------------------------------------
|
|
# Tuples with named fields for clarity
|
|
|
|
print("\n--- Named Tuples ---")
|
|
|
|
from collections import namedtuple
|
|
|
|
# Define a named tuple type
|
|
Person = namedtuple("Person", ["name", "age", "city"])
|
|
|
|
# Create instances
|
|
person1 = Person("Baraa", 25, "Gaza")
|
|
person2 = Person(name="Sara", age=30, city="Cairo")
|
|
|
|
print(f"Person 1: {person1}")
|
|
print(f"Person 2: {person2}")
|
|
|
|
# Access by name or index
|
|
print(f"\nAccess by name: {person1.name}")
|
|
print(f"Access by index: {person1[0]}")
|
|
|
|
# Named tuple is still immutable
|
|
# person1.age = 26 # This would raise an error
|
|
|
|
# Convert to dictionary
|
|
print(f"As dict: {person1._asdict()}")
|
|
|
|
# Create from dictionary
|
|
data = {"name": "Ali", "age": 28, "city": "Dubai"}
|
|
person3 = Person(**data)
|
|
print(f"From dict: {person3}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 10. When to Use Tuples vs Lists
|
|
# -----------------------------------------------------------------------------
|
|
|
|
print("\n--- Tuples vs Lists ---")
|
|
|
|
# Use TUPLES when:
|
|
# 1. Data should not change
|
|
rgb_red = (255, 0, 0) # Color values shouldn't change
|
|
|
|
# 2. Using as dictionary keys
|
|
grid_positions = {(0, 0): "start", (9, 9): "end"}
|
|
|
|
# 3. Returning multiple values from functions
|
|
def get_point(): return (10, 20)
|
|
|
|
# 4. Heterogeneous data (different types, specific meaning)
|
|
person = ("John", 30, "Engineer") # name, age, job
|
|
|
|
# Use LISTS when:
|
|
# 1. Data will be modified
|
|
shopping_cart = ["apple", "bread"] # Items will be added/removed
|
|
|
|
# 2. Homogeneous collection
|
|
scores = [85, 90, 78, 92] # All same type, will be processed together
|
|
|
|
# 3. Order matters and you need to sort/shuffle
|
|
rankings = [3, 1, 4, 1, 5]
|
|
rankings.sort() # Lists are sortable in place
|
|
|
|
print("Summary:")
|
|
print(" Tuples: Immutable, hashable, for fixed data")
|
|
print(" Lists: Mutable, flexible, for changing data")
|
|
|
|
# Performance note: Tuples are slightly faster and use less memory
|
|
import sys
|
|
list_size = sys.getsizeof([1, 2, 3, 4, 5])
|
|
tuple_size = sys.getsizeof((1, 2, 3, 4, 5))
|
|
print(f"\nMemory: List={list_size} bytes, Tuple={tuple_size} bytes")
|