Bài 6: Tuples - Bộ Dữ Liệu Bất Biến

Mục Tiêu Bài Học

Sau khi hoàn thành bài này, bạn sẽ:

  • ✅ Hiểu tuple là gì và tính chất immutable
  • ✅ Tạo và truy cập tuple
  • ✅ Sử dụng tuple packing và unpacking
  • ✅ Phân biệt tuple vs list
  • ✅ Biết khi nào nên dùng tuple

Tuple Là Gì?

Tuple là một collection có thứ tự, không thể thay đổi (immutable), và cho phép các phần tử trùng lặp.

# Tuple với dấu ngoặc tròn ()fruits = ("apple", "banana", "orange")numbers = (1, 2, 3, 4, 5)mixed = (1, "Hello", 3.14, True) print(type(fruits))  # <class 'tuple'>

Immutable - Không thể thay đổi:

my_tuple = (1, 2, 3) # Không thể thay đổi phần tử# my_tuple[0] = 10  # TypeError: 'tuple' object does not support item assignment # Không thể thêm hoặc xóa# my_tuple.append(4)  # AttributeError# my_tuple.remove(1)  # AttributeError # Nhưng có thể tạo tuple mớinew_tuple = my_tuple + (4, 5)print(new_tuple)  # (1, 2, 3, 4, 5)

Tạo Tuple

Cách 1: Dùng Dấu Ngoặc Tròn ()

# Tuple thông thườngcolors = ("red", "green", "blue")numbers = (1, 2, 3, 4, 5) # Empty tupleempty = ()print(len(empty))  # 0 # Single element tuple (cần dấu phẩy!)single = (5,)       # ✅ Tuplenot_tuple = (5)     # ❌ Này là int, không phải tuple! print(type(single))     # <class 'tuple'>print(type(not_tuple))  # <class 'int'> # Multi-line tupleperson = (    "Alice",    25,    "[email protected]",    "USA")

Cách 2: Không Cần Dấu Ngoặc (Tuple Packing)

# Tuple packing - tự động tạo tuplecoordinates = 10, 20, 30print(type(coordinates))  # <class 'tuple'>print(coordinates)        # (10, 20, 30) # Thường dùng trong returndef get_user():    return "Alice", 25, "[email protected]" user = get_user()print(user)  # ('Alice', 25, '[email protected]')

Cách 3: tuple() Constructor

# Từ listmy_list = [1, 2, 3]my_tuple = tuple(my_list)print(my_tuple)  # (1, 2, 3) # Từ stringchars = tuple("Python")print(chars)  # ('P', 'y', 't', 'h', 'o', 'n') # Từ rangenumbers = tuple(range(5))print(numbers)  # (0, 1, 2, 3, 4)

Truy Cập Phần Tử

Giống như list, tuple hỗ trợ indexing và slicing.

Indexing

fruits = ("apple", "banana", "orange", "mango", "grape")#          0        1         2         3        4       (positive)#         -5       -4        -3        -2       -1       (negative) # Positive indexingprint(fruits[0])   # appleprint(fruits[2])   # orange # Negative indexingprint(fruits[-1])  # grapeprint(fruits[-2])  # mango # Nested tuplenested = ((1, 2), (3, 4), (5, 6))print(nested[0])      # (1, 2)print(nested[0][1])   # 2print(nested[2][0])   # 5

Slicing

numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) # [start:stop]print(numbers[2:5])    # (2, 3, 4)print(numbers[:3])     # (0, 1, 2)print(numbers[5:])     # (5, 6, 7, 8, 9) # [start:stop:step]print(numbers[::2])    # (0, 2, 4, 6, 8)print(numbers[::-1])   # (9, 8, 7, 6, 5, 4, 3, 2, 1, 0) # Negative indicesprint(numbers[-3:])    # (7, 8, 9)

Tuple Packing và Unpacking

Tuple Packing

Packing là gộp nhiều giá trị thành một tuple.

# Packingpoint = 10, 20print(point)  # (10, 20) # Function return multiple valuesdef get_stats(numbers):    return min(numbers), max(numbers), sum(numbers) stats = get_stats([1, 2, 3, 4, 5])print(stats)  # (1, 5, 15)

Tuple Unpacking

Unpacking là gán từng phần tử của tuple cho các biến riêng.

# Basic unpackingpoint = (10, 20)x, y = pointprint(f"x={x}, y={y}")  # x=10, y=20 # Unpacking trong one linename, age, email = ("Alice", 25, "[email protected]")print(name)   # Aliceprint(age)    # 25print(email)  # [email protected] # Swap variablesa, b = 10, 20a, b = b, a  # Swap dùng tuple unpackingprint(a, b)  # 20 10

Unpacking với * (Extended Unpacking)

# * để lấy phần còn lạifirst, *rest = (1, 2, 3, 4, 5)print(first)  # 1print(rest)   # [2, 3, 4, 5] # Lấy first và lastfirst, *middle, last = (1, 2, 3, 4, 5)print(first)   # 1print(middle)  # [2, 3, 4]print(last)    # 5 # Skip values với _first, _, third, *_ = (1, 2, 3, 4, 5)print(first, third)  # 1 3 # Unpacking trong looppeople = [    ("Alice", 25),    ("Bob", 30),    ("Charlie", 35)] for name, age in people:    print(f"{name} is {age} years old")

Tuple Operations

Concatenation và Repetition

# Concatenation với +tuple1 = (1, 2, 3)tuple2 = (4, 5, 6)result = tuple1 + tuple2print(result)  # (1, 2, 3, 4, 5, 6) # Repetition với *pattern = ("A", "B")repeated = pattern * 3print(repeated)  # ('A', 'B', 'A', 'B', 'A', 'B') # ❌ Không thể dùng +=, *= trực tiếp (tạo tuple mới)my_tuple = (1, 2)my_tuple = my_tuple + (3,)  # Phải gán lạiprint(my_tuple)  # (1, 2, 3)

Membership Test

fruits = ("apple", "banana", "orange") # in operatorprint("apple" in fruits)      # Trueprint("grape" in fruits)      # Falseprint("grape" not in fruits)  # True # Nested tuplematrix = ((1, 2), (3, 4), (5, 6))print((3, 4) in matrix)  # True

Comparison

# Lexicographical comparisonprint((1, 2, 3) < (1, 2, 4))     # Trueprint((1, 2, 3) == (1, 2, 3))    # Trueprint(("a", "b") < ("a", "c"))   # True # So sánh element đầu tiên trướcprint((2, 0) > (1, 100))         # True (2 > 1)

Tuple Methods

Tuple chỉ có 2 methods vì immutable.

1. count() - Đếm phần tử

numbers = (1, 2, 2, 3, 2, 4, 2, 5) count = numbers.count(2)print(count)  # 4 # Đếm trong nested tuplenested = ((1, 2), (3, 4), (1, 2))print(nested.count((1, 2)))  # 2

2. index() - Tìm vị trí

fruits = ("apple", "banana", "orange", "banana") # Tìm index đầu tiênpos = fruits.index("banana")print(pos)  # 1 # Tìm từ vị trí cụ thểpos2 = fruits.index("banana", 2)  # Tìm từ index 2print(pos2)  # 3 # Lỗi nếu không tìm thấy# fruits.index("grape")  # ValueError! # Cách an toànif "grape" in fruits:    pos = fruits.index("grape")else:    print("Not found")

Built-in Functions với Tuple

numbers = (5, 2, 8, 1, 9, 3) # len() - Độ dàiprint(len(numbers))  # 6 # min(), max() - Nhỏ nhất, lớn nhấtprint(min(numbers))  # 1print(max(numbers))  # 9 # sum() - Tổngprint(sum(numbers))  # 28 # sorted() - Sắp xếp (trả về list!)sorted_nums = sorted(numbers)print(sorted_nums)   # [1, 2, 3, 5, 8, 9]print(type(sorted_nums))  # <class 'list'> # Trả về tuple thì phải convertsorted_tuple = tuple(sorted(numbers))print(sorted_tuple)  # (1, 2, 3, 5, 8, 9) # any(), all()print(any(numbers))  # True (có ít nhất 1 truthy)print(all(numbers))  # True (tất cả truthy) bools = (True, False, True)print(any(bools))    # Trueprint(all(bools))    # False

Tuple vs List

Feature Tuple List
Syntax () []
Mutable ❌ Immutable ✅ Mutable
Performance ⚡ Nhanh hơn 🐌 Chậm hơn
Memory 💾 Ít hơn 💾 Nhiều hơn
Methods 2 methods 11+ methods
Use Case Fixed data Dynamic data
Hash ✅ Hashable ❌ Not hashable
import sys # Memory comparisonmy_list = [1, 2, 3, 4, 5]my_tuple = (1, 2, 3, 4, 5) print(f"List size: {sys.getsizeof(my_list)} bytes")print(f"Tuple size: {sys.getsizeof(my_tuple)} bytes")# Tuple nhỏ hơn! # Hashable - tuple có thể làm dictionary key# List không thể!point_dict = {    (0, 0): "Origin",    (1, 1): "Point A",    (2, 2): "Point B"}print(point_dict[(0, 0)])  # Origin # ❌ List không làm được# bad_dict = {[0, 0]: "Origin"}  # TypeError!

Khi nào dùng Tuple:

  • ✅ Data không thay đổi (coordinates, RGB colors, dates)
  • ✅ Function return multiple values
  • ✅ Dictionary keys
  • ✅ Protect data from modification
  • ✅ Performance critical (faster than list)

Khi nào dùng List:

  • ✅ Data cần thay đổi (add, remove, modify)
  • ✅ Unknown size
  • ✅ Cần methods như append(), remove(), sort()

Named Tuples

Named tuples là tuple với tên field, giống struct.

from collections import namedtuple # Define named tuplePoint = namedtuple('Point', ['x', 'y']) # Tạo instancep1 = Point(10, 20)p2 = Point(x=30, y=40) # Truy cập bằng tên (readable!)print(p1.x, p1.y)  # 10 20 # Vẫn truy cập được bằng indexprint(p1[0], p1[1])  # 10 20 # Unpacking vẫn hoạt độngx, y = p1print(x, y)  # 10 20 # Immutable như tuple thông thường# p1.x = 100  # AttributeError! # Use case: PersonPerson = namedtuple('Person', ['name', 'age', 'email'])alice = Person("Alice", 25, "[email protected]") print(alice.name)   # Aliceprint(alice.age)    # 25print(alice.email)  # [email protected] # Convert to dictprint(alice._asdict())# {'name': 'Alice', 'age': 25, 'email': '[email protected]'}

Ví Dụ Thực Tế

1. RGB Color System

# RGB colors as tuplesRED = (255, 0, 0)GREEN = (0, 255, 0)BLUE = (0, 0, 255)WHITE = (255, 255, 255)BLACK = (0, 0, 0) def mix_colors(color1, color2):    """Mix 2 RGB colors (average)"""    r1, g1, b1 = color1    r2, g2, b2 = color2        r = (r1 + r2) // 2    g = (g1 + g2) // 2    b = (b1 + b2) // 2        return (r, g, b) purple = mix_colors(RED, BLUE)print(f"Purple: RGB{purple}")  # Purple: RGB(127, 0, 127) yellow = mix_colors(RED, GREEN)print(f"Yellow: RGB{yellow}")  # Yellow: RGB(127, 127, 0)

2. Database Records

# Database row as tuple (immutable)records = [    (1, "Alice", "[email protected]", 25),    (2, "Bob", "[email protected]", 30),    (3, "Charlie", "[email protected]", 35)] # Process recordsprint("User List:")for user_id, name, email, age in records:    print(f"  [{user_id}] {name:<10} ({age}) - {email}") # Filter by ageyoung_users = [r for r in records if r[3] < 30]print(f"\nUsers under 30: {len(young_users)}") # Sort by agesorted_records = sorted(records, key=lambda x: x[3])print("\nSorted by age:")for record in sorted_records:    print(f"  {record[1]}: {record[3]} years old")

3. Coordinate System

# 2D points as tuplespoints = [    (0, 0),    # Origin    (3, 4),    (1, 1),    (5, 12),    (8, 15)] def distance_from_origin(point):    """Calculate distance from origin"""    x, y = point    return (x**2 + y**2) ** 0.5 # Calculate distancesprint("Distances from origin:")for point in points:    dist = distance_from_origin(point)    print(f"  {point} -> {dist:.2f}") # Find closest point to originclosest = min(points, key=distance_from_origin)print(f"\nClosest point: {closest}") # Find farthest pointfarthest = max(points, key=distance_from_origin)print(f"Farthest point: {farthest}")

4. Function Return Multiple Values

def analyze_list(numbers):    """Analyze list and return stats as tuple"""    if not numbers:        return None        total = sum(numbers)    count = len(numbers)    average = total / count    minimum = min(numbers)    maximum = max(numbers)        return (total, count, average, minimum, maximum) # Use the functiondata = [85, 90, 78, 92, 88, 76, 95]result = analyze_list(data) if result:    total, count, avg, min_val, max_val = result    print(f"Count: {count}")    print(f"Total: {total}")    print(f"Average: {avg:.2f}")    print(f"Range: {min_val} - {max_val}")

5. Config Settings

# Configuration as tuple (immutable, prevent accidental changes)DATABASE_CONFIG = (    "localhost",    # host    5432,          # port    "mydb",        # database    "user",        # username    "password"     # password) # Unpack confighost, port, db, user, password = DATABASE_CONFIG def connect_database():    """Simulate database connection"""    print(f"Connecting to {db} at {host}:{port}")    print(f"Username: {user}")    return f"Connected to {host}:{port}/{db}" connection = connect_database()print(connection) # ✅ Config không thể bị thay đổi accidentally# DATABASE_CONFIG[0] = "newhost"  # TypeError!

Bài Tập Thực Hành

Bài 1: Tuple Operations

Tạo 2 tuples:

  • t1 = (1, 2, 3)
  • t2 = (4, 5, 6)
  • Nối chúng
  • Lặp lại t1 3 lần
  • Kiểm tra 3 có trong t1 không

Bài 2: Swap Variables

Viết chương trình swap 3 variables:

  • a = 1, b = 2, c = 3
  • Sau swap: a = 2, b = 3, c = 1

Bài 3: Statistics Function

Viết function get_stats(numbers) return tuple:

  • (min, max, average, median)

Bài 4: Point Distance

Cho list points (tuples), tìm:

  • 2 điểm gần nhau nhất
  • 2 điểm xa nhau nhất

Bài 5: Named Tuple

Tạo named tuple Student với:

  • name, age, grade
  • Tạo list 5 students
  • Sort by grade
  • Tìm student cao điểm nhất

Tóm Tắt

✅ Tuple: ordered, immutable, allow duplicates
✅ Syntax: () hoặc không cần ngoặc
✅ Single element: (5,) cần dấu phẩy
✅ Packing: point = 10, 20
✅ Unpacking: x, y = point
✅ Methods: count(), index()
✅ Faster và ít memory hơn list
✅ Named tuples: readable code

Bài Tiếp Theo

Bài 7: Dictionaries - Key-value pairs, dictionary methods, và dictionary comprehension.


Remember:

  • Tuples are immutable - không thể thay đổi sau khi tạo
  • Dùng tuple cho data cố định (coordinates, colors, config)
  • Tuple unpacking rất mạnh mẽ và pythonic
  • Named tuples cho code readable hơn
  • Tuple nhanh hơn list - dùng khi không cần modify!