Bài 15: Object-Oriented Programming - OOP (Phần 2)

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

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

  • ✅ Hiểu và implement Inheritance
  • ✅ Sử dụng super() và method overriding
  • ✅ Làm việc với Polymorphism
  • ✅ Implement Encapsulation
  • ✅ Dùng @property decorator
  • ✅ Sử dụng Magic Methods

Inheritance (Kế Thừa)

Inheritance cho phép class kế thừa attributes và methods từ class khác.

Basic Inheritance

# Parent class (Base class / Superclass)class Animal:    """Base animal class."""        def __init__(self, name, age):        self.name = name        self.age = age        def make_sound(self):        return "Some generic sound"        def info(self):        return f"{self.name} is {self.age} years old" # Child class (Derived class / Subclass)class Dog(Animal):    """Dog inherits from Animal."""        def make_sound(self):        return "Woof!"        def fetch(self):        return f"{self.name} is fetching the ball" class Cat(Animal):    """Cat inherits from Animal."""        def make_sound(self):        return "Meow!"        def climb(self):        return f"{self.name} is climbing a tree" # Usagedog = Dog("Buddy", 3)cat = Cat("Whiskers", 2) print(dog.info())         # Buddy is 3 years old (inherited)print(dog.make_sound())   # Woof! (overridden)print(dog.fetch())        # Buddy is fetching the ball print(cat.info())         # Whiskers is 2 years old (inherited)print(cat.make_sound())   # Meow! (overridden)print(cat.climb())        # Whiskers is climbing a tree # Check inheritanceprint(isinstance(dog, Dog))      # Trueprint(isinstance(dog, Animal))   # Trueprint(issubclass(Dog, Animal))   # True

super() Function

super() gọi methods từ parent class.

class Vehicle:    """Base vehicle class."""        def __init__(self, brand, model, year):        self.brand = brand        self.model = model        self.year = year        def info(self):        return f"{self.year} {self.brand} {self.model}" class Car(Vehicle):    """Car class."""        def __init__(self, brand, model, year, num_doors):        # Call parent __init__        super().__init__(brand, model, year)        self.num_doors = num_doors        def info(self):        # Call parent info() and extend        base_info = super().info()        return f"{base_info} - {self.num_doors} doors" class ElectricCar(Car):    """Electric car class."""        def __init__(self, brand, model, year, num_doors, battery_size):        super().__init__(brand, model, year, num_doors)        self.battery_size = battery_size        def info(self):        base_info = super().info()        return f"{base_info} - {self.battery_size}kWh battery" # Usagecar = Car("Toyota", "Camry", 2024, 4)print(car.info())  # 2024 Toyota Camry - 4 doors ev = ElectricCar("Tesla", "Model 3", 2024, 4, 75)print(ev.info())   # 2024 Tesla Model 3 - 4 doors - 75kWh battery

Method Overriding

class Employee:    """Base employee class."""        def __init__(self, name, salary):        self.name = name        self.salary = salary        def get_bonus(self):        """Calculate bonus."""        return self.salary * 0.1 class Manager(Employee):    """Manager with higher bonus."""        def __init__(self, name, salary, team_size):        super().__init__(name, salary)        self.team_size = team_size        def get_bonus(self):        """Override - higher bonus."""        base_bonus = super().get_bonus()        team_bonus = self.team_size * 1000        return base_bonus + team_bonus class Developer(Employee):    """Developer with project bonus."""        def __init__(self, name, salary, projects_completed):        super().__init__(name, salary)        self.projects_completed = projects_completed        def get_bonus(self):        """Override - project-based bonus."""        base_bonus = super().get_bonus()        project_bonus = self.projects_completed * 500        return base_bonus + project_bonus # Usageemp = Employee("Alice", 50000)mgr = Manager("Bob", 80000, 5)dev = Developer("Charlie", 70000, 10) print(f"{emp.name}: ${emp.get_bonus():.2f}")    # Alice: $5000.00print(f"{mgr.name}: ${mgr.get_bonus():.2f}")    # Bob: $13000.00print(f"{dev.name}: ${dev.get_bonus():.2f}")    # Charlie: $12000.00

Multiple Inheritance

class Flyable:    """Flying capability."""        def fly(self):        return f"{self.name} is flying" class Swimmable:    """Swimming capability."""        def swim(self):        return f"{self.name} is swimming" class Duck(Animal, Flyable, Swimmable):    """Duck can fly and swim."""        def __init__(self, name, age):        super().__init__(name, age)        def make_sound(self):        return "Quack!" # Usageduck = Duck("Donald", 2)print(duck.info())        # Donald is 2 years oldprint(duck.make_sound())  # Quack!print(duck.fly())         # Donald is flyingprint(duck.swim())        # Donald is swimming # MRO - Method Resolution Orderprint(Duck.__mro__)# (<class 'Duck'>, <class 'Animal'>, <class 'Flyable'>, #  <class 'Swimmable'>, <class 'object'>)

Polymorphism

Polymorphism cho phép objects của different classes được treat như cùng type.

Method Polymorphism

class Shape:    """Base shape class."""        def area(self):        raise NotImplementedError("Subclass must implement")        def perimeter(self):        raise NotImplementedError("Subclass must implement") class Rectangle(Shape):    """Rectangle shape."""        def __init__(self, width, height):        self.width = width        self.height = height        def area(self):        return self.width * self.height        def perimeter(self):        return 2 * (self.width + self.height) class Circle(Shape):    """Circle shape."""        def __init__(self, radius):        self.radius = radius        def area(self):        return 3.14159 * self.radius ** 2        def perimeter(self):        return 2 * 3.14159 * self.radius class Triangle(Shape):    """Triangle shape."""        def __init__(self, base, height, side1, side2, side3):        self.base = base        self.height = height        self.side1 = side1        self.side2 = side2        self.side3 = side3        def area(self):        return 0.5 * self.base * self.height        def perimeter(self):        return self.side1 + self.side2 + self.side3 # Polymorphism in actionshapes = [    Rectangle(5, 10),    Circle(7),    Triangle(6, 8, 5, 9, 10)] print("Shape Areas:")for shape in shapes:    print(f"{shape.__class__.__name__}: {shape.area():.2f}") # Output:# Rectangle: 50.00# Circle: 153.94# Triangle: 24.00

Duck Typing

# "If it walks like a duck and quacks like a duck, it's a duck" class FileWriter:    """Write to file."""        def write(self, data):        with open('output.txt', 'a') as f:            f.write(data + '\n') class ConsoleWriter:    """Write to console."""        def write(self, data):        print(data) class LogWriter:    """Write to log."""        def write(self, data):        import logging        logging.info(data) def log_message(writer, message):    """    Log message using any writer.    Works with anything that has write() method.    """    writer.write(f"[LOG] {message}") # Usage - all work the same waylog_message(FileWriter(), "File message")log_message(ConsoleWriter(), "Console message")log_message(LogWriter(), "Log message")

Encapsulation

Encapsulation ẩn internal details, expose public interface.

Private Attributes

class BankAccount:    """Bank account with private balance."""        def __init__(self, owner, balance=0):        self.owner = owner        self.__balance = balance  # Private attribute        def deposit(self, amount):        """Public method to deposit."""        if amount > 0:            self.__balance += amount            return f"Deposited ${amount}"        return "Invalid amount"        def withdraw(self, amount):        """Public method to withdraw."""        if amount > self.__balance:            return "Insufficient funds"        if amount > 0:            self.__balance -= amount            return f"Withdrew ${amount}"        return "Invalid amount"        def get_balance(self):        """Public method to check balance."""        return self.__balance # Usageaccount = BankAccount("Alice", 1000) # ✅ Public interfaceprint(account.deposit(500))      # Deposited $500print(account.get_balance())     # 1500 # ❌ Cannot access private attribute# print(account.__balance)  # AttributeError # But can access via name mangling (not recommended!)print(account._BankAccount__balance)  # 1500 (name mangling)

Protected Attributes

class Person:    """Person with protected attributes."""        def __init__(self, name, age):        self.name = name        self._age = age  # Protected (convention)        def get_age(self):        return self._age class Student(Person):    """Student can access protected attributes."""        def __init__(self, name, age, student_id):        super().__init__(name, age)        self.student_id = student_id        def info(self):        # Can access _age from parent        return f"{self.name}, {self._age} years old, ID: {self.student_id}" # Usagestudent = Student("Alice", 20, "S001")print(student.info())      # Alice, 20 years old, ID: S001print(student._age)        # 20 (accessible but not recommended)

Property Decorators

@property tạo computed attributes với getter/setter.

Read-only Property

class Circle:    """Circle with computed properties."""        def __init__(self, radius):        self._radius = radius        @property    def radius(self):        """Getter for radius."""        return self._radius        @property    def diameter(self):        """Computed property."""        return self._radius * 2        @property    def area(self):        """Computed property."""        return 3.14159 * self._radius ** 2        @property    def circumference(self):        """Computed property."""        return 2 * 3.14159 * self._radius # Usagecircle = Circle(5) print(circle.radius)          # 5print(circle.diameter)        # 10print(circle.area)            # 78.53975print(circle.circumference)   # 31.4159 # Use like attributes (no parentheses!)# circle.area() ❌# circle.area   ✅

Getter and Setter

class Temperature:    """Temperature with validation."""        def __init__(self, celsius):        self._celsius = celsius        @property    def celsius(self):        """Get temperature in Celsius."""        return self._celsius        @celsius.setter    def celsius(self, value):        """Set temperature in Celsius with validation."""        if value < -273.15:            raise ValueError("Temperature below absolute zero!")        self._celsius = value        @property    def fahrenheit(self):        """Get temperature in Fahrenheit."""        return self._celsius * 9/5 + 32        @fahrenheit.setter    def fahrenheit(self, value):        """Set temperature using Fahrenheit."""        self.celsius = (value - 32) * 5/9        @property    def kelvin(self):        """Get temperature in Kelvin."""        return self._celsius + 273.15 # Usagetemp = Temperature(25) print(f"Celsius: {temp.celsius}")       # 25print(f"Fahrenheit: {temp.fahrenheit}") # 77.0print(f"Kelvin: {temp.kelvin}")         # 298.15 # Set using different unitstemp.fahrenheit = 86print(f"Celsius: {temp.celsius}")       # 30.0 temp.celsius = 0print(f"Fahrenheit: {temp.fahrenheit}") # 32.0 # Validation works# temp.celsius = -300  # ValueError!

Deleter

class User:    """User with deletable email."""        def __init__(self, username, email):        self.username = username        self._email = email        @property    def email(self):        """Get email."""        return self._email        @email.setter    def email(self, value):        """Set email with validation."""        if '@' not in value:            raise ValueError("Invalid email")        self._email = value        @email.deleter    def email(self):        """Delete email."""        print(f"Deleting email for {self.username}")        self._email = None # Usageuser = User("alice", "[email protected]") print(user.email)              # [email protected]user.email = "[email protected]"print(user.email)              # [email protected] del user.email                 # Deleting email for aliceprint(user.email)              # None

Magic Methods

Magic methods (dunder methods) customize object behavior.

Comparison Methods

class Person:    """Person with comparison."""        def __init__(self, name, age):        self.name = name        self.age = age        def __eq__(self, other):        """Equal to."""        return self.age == other.age        def __lt__(self, other):        """Less than."""        return self.age < other.age        def __le__(self, other):        """Less than or equal."""        return self.age <= other.age        def __gt__(self, other):        """Greater than."""        return self.age > other.age        def __ge__(self, other):        """Greater than or equal."""        return self.age >= other.age        def __ne__(self, other):        """Not equal."""        return self.age != other.age        def __str__(self):        return f"{self.name} ({self.age})" # Usageperson1 = Person("Alice", 25)person2 = Person("Bob", 30)person3 = Person("Charlie", 25) print(person1 == person3)  # True (same age)print(person1 < person2)   # Trueprint(person2 > person1)   # True # Sortingpeople = [person2, person1, person3]people.sort()  # Uses __lt__print([str(p) for p in people])# ['Alice (25)', 'Charlie (25)', 'Bob (30)']

Arithmetic Methods

class Vector:    """2D Vector with arithmetic."""        def __init__(self, x, y):        self.x = x        self.y = y        def __add__(self, other):        """Add vectors."""        return Vector(self.x + other.x, self.y + other.y)        def __sub__(self, other):        """Subtract vectors."""        return Vector(self.x - other.x, self.y - other.y)        def __mul__(self, scalar):        """Multiply by scalar."""        return Vector(self.x * scalar, self.y * scalar)        def __truediv__(self, scalar):        """Divide by scalar."""        return Vector(self.x / scalar, self.y / scalar)        def __str__(self):        return f"Vector({self.x}, {self.y})"        def __repr__(self):        return f"Vector({self.x}, {self.y})" # Usagev1 = Vector(2, 3)v2 = Vector(4, 5) v3 = v1 + v2print(v3)  # Vector(6, 8) v4 = v2 - v1print(v4)  # Vector(2, 2) v5 = v1 * 3print(v5)  # Vector(6, 9) v6 = v2 / 2print(v6)  # Vector(2.0, 2.5)

Container Methods

class TodoList:    """Todo list with container methods."""        def __init__(self):        self.tasks = []        def __len__(self):        """Return number of tasks."""        return len(self.tasks)        def __getitem__(self, index):        """Get task by index."""        return self.tasks[index]        def __setitem__(self, index, value):        """Set task by index."""        self.tasks[index] = value        def __delitem__(self, index):        """Delete task by index."""        del self.tasks[index]        def __contains__(self, item):        """Check if task exists."""        return item in self.tasks        def __iter__(self):        """Make iterable."""        return iter(self.tasks)        def add(self, task):        """Add task."""        self.tasks.append(task)        def __str__(self):        return f"TodoList({len(self)} tasks)" # Usagetodo = TodoList()todo.add("Learn Python")todo.add("Build project")todo.add("Deploy app") print(len(todo))              # 3print(todo[0])                # Learn Pythonprint("Build project" in todo)  # True # Iteratefor task in todo:    print(f"- {task}") # Modifytodo[1] = "Build awesome project"del todo[2]print(len(todo))              # 2

Call Method

class Multiplier:    """Callable multiplier."""        def __init__(self, factor):        self.factor = factor        def __call__(self, value):        """Make instance callable."""        return value * self.factor # Usagedouble = Multiplier(2)triple = Multiplier(3) print(double(5))   # 10print(triple(5))   # 15 # Works like a function!numbers = [1, 2, 3, 4, 5]doubled = list(map(double, numbers))print(doubled)     # [2, 4, 6, 8, 10]

Ví Dụ Thực Tế

1. E-commerce Product System

class Product:    """Base product class."""        def __init__(self, name, price):        self.name = name        self._price = price        @property    def price(self):        """Get price."""        return self._price        @price.setter    def price(self, value):        """Set price with validation."""        if value < 0:            raise ValueError("Price cannot be negative")        self._price = value        def calculate_total(self, quantity):        """Calculate total price."""        return self.price * quantity        def __str__(self):        return f"{self.name} - ${self.price:.2f}" class PhysicalProduct(Product):    """Physical product with shipping."""        def __init__(self, name, price, weight):        super().__init__(name, price)        self.weight = weight        def calculate_shipping(self):        """Calculate shipping cost."""        return self.weight * 0.5        def calculate_total(self, quantity):        """Override to include shipping."""        product_total = super().calculate_total(quantity)        shipping = self.calculate_shipping()        return product_total + shipping class DigitalProduct(Product):    """Digital product with download."""        def __init__(self, name, price, file_size):        super().__init__(name, price)        self.file_size = file_size        def get_download_link(self):        """Generate download link."""        return f"https://download.example.com/{self.name.lower().replace(' ', '-')}" class SubscriptionProduct(Product):    """Subscription product."""        def __init__(self, name, monthly_price, duration_months):        super().__init__(name, monthly_price)        self.duration_months = duration_months        def calculate_total(self, quantity=1):        """Override for subscription."""        return self.price * self.duration_months # Usageproducts = [    PhysicalProduct("Laptop", 999.99, 2.5),    DigitalProduct("Python Course", 49.99, 500),    SubscriptionProduct("Netflix", 15.99, 12)] print("Products:")for product in products:    print(f"  {product}")    if isinstance(product, PhysicalProduct):        print(f"    Shipping: ${product.calculate_shipping():.2f}")    elif isinstance(product, DigitalProduct):        print(f"    Download: {product.get_download_link()}")    elif isinstance(product, SubscriptionProduct):        print(f"    Duration: {product.duration_months} months")    print(f"    Total: ${product.calculate_total(1):.2f}")

2. Employee Management System

class Employee:    """Base employee class."""        employee_count = 0        def __init__(self, name, employee_id, base_salary):        self.name = name        self.employee_id = employee_id        self._base_salary = base_salary        Employee.employee_count += 1        @property    def base_salary(self):        """Get base salary."""        return self._base_salary        @base_salary.setter    def base_salary(self, value):        """Set base salary with validation."""        if value < 0:            raise ValueError("Salary cannot be negative")        self._base_salary = value        def calculate_salary(self):        """Calculate total salary."""        return self.base_salary        def __str__(self):        return f"{self.name} ({self.employee_id})"        def __repr__(self):        return f"Employee('{self.name}', '{self.employee_id}', {self.base_salary})" class FullTimeEmployee(Employee):    """Full-time employee with benefits."""        def __init__(self, name, employee_id, base_salary, benefits):        super().__init__(name, employee_id, base_salary)        self.benefits = benefits        def calculate_salary(self):        """Override with benefits."""        return self.base_salary + self.benefits class PartTimeEmployee(Employee):    """Part-time employee with hourly rate."""        def __init__(self, name, employee_id, hourly_rate, hours_worked):        super().__init__(name, employee_id, 0)        self.hourly_rate = hourly_rate        self.hours_worked = hours_worked        def calculate_salary(self):        """Override with hours."""        return self.hourly_rate * self.hours_worked class Contractor(Employee):    """Contractor with project-based pay."""        def __init__(self, name, employee_id, project_fee, projects_completed):        super().__init__(name, employee_id, 0)        self.project_fee = project_fee        self.projects_completed = projects_completed        def calculate_salary(self):        """Override with project fee."""        return self.project_fee * self.projects_completed # Usageemployees = [    FullTimeEmployee("Alice", "FT001", 50000, 10000),    PartTimeEmployee("Bob", "PT001", 25, 80),    Contractor("Charlie", "CT001", 5000, 3)] print(f"Total Employees: {Employee.employee_count}\n")print("Payroll:")total_payroll = 0for emp in employees:    salary = emp.calculate_salary()    total_payroll += salary    print(f"  {emp}: ${salary:.2f}") print(f"\nTotal Payroll: ${total_payroll:.2f}")

3. Game Character System

class Character:    """Base game character."""        def __init__(self, name, health, attack, defense):        self.name = name        self._health = health        self.max_health = health        self.attack = attack        self.defense = defense        @property    def health(self):        """Get health."""        return self._health        @health.setter    def health(self, value):        """Set health (0 to max)."""        self._health = max(0, min(value, self.max_health))        @property    def is_alive(self):        """Check if character is alive."""        return self._health > 0        def take_damage(self, damage):        """Take damage."""        actual_damage = max(0, damage - self.defense)        self.health -= actual_damage        return actual_damage        def heal(self, amount):        """Heal character."""        old_health = self.health        self.health += amount        return self.health - old_health        def attack_target(self, target):        """Attack another character."""        damage = target.take_damage(self.attack)        return damage        def __str__(self):        return f"{self.name} (HP: {self.health}/{self.max_health})" class Warrior(Character):    """Warrior with special attack."""        def __init__(self, name):        super().__init__(name, health=150, attack=20, defense=10)        self.rage = 0        def take_damage(self, damage):        """Override to build rage."""        actual_damage = super().take_damage(damage)        self.rage += actual_damage // 5        return actual_damage        def special_attack(self, target):        """Powerful attack using rage."""        if self.rage >= 20:            damage = target.take_damage(self.attack * 2)            self.rage = 0            return damage, "CRITICAL HIT!"        return 0, "Not enough rage" class Mage(Character):    """Mage with mana and spells."""        def __init__(self, name):        super().__init__(name, health=80, attack=30, defense=5)        self.mana = 100        self.max_mana = 100        def cast_fireball(self, target):        """Cast fireball spell."""        if self.mana >= 30:            self.mana -= 30            damage = target.take_damage(self.attack * 1.5)            return damage, "Fireball!"        return 0, "Not enough mana"        def regenerate_mana(self):        """Regenerate mana."""        self.mana = min(self.mana + 20, self.max_mana) class Healer(Character):    """Healer class."""        def __init__(self, name):        super().__init__(name, health=100, attack=10, defense=8)        self.mana = 100        def heal_target(self, target):        """Heal another character."""        if self.mana >= 20:            self.mana -= 20            amount = target.heal(40)            return amount, "Healing!"        return 0, "Not enough mana" # Usage - Battle simulationwarrior = Warrior("Conan")mage = Mage("Gandalf")healer = Healer("Mercy") print("=== Battle Start ===")print(warrior)print(mage)print(healer) print("\n--- Round 1 ---")damage = warrior.attack_target(mage)print(f"{warrior.name} attacks {mage.name} for {damage} damage")print(mage) damage, msg = mage.cast_fireball(warrior)print(f"{mage.name} casts fireball on {warrior.name} for {damage} damage - {msg}")print(warrior) amount, msg = healer.heal_target(mage)print(f"{healer.name} heals {mage.name} for {amount} HP - {msg}")print(mage) print("\n--- Round 2 ---")damage, msg = warrior.special_attack(mage)print(f"{warrior.name} special attack on {mage.name} for {damage} damage - {msg}")print(mage)

4. File System Abstraction

from abc import ABC, abstractmethodfrom datetime import datetime class FileSystemItem(ABC):    """Abstract base class for file system items."""        def __init__(self, name):        self.name = name        self.created_at = datetime.now()        @abstractmethod    def get_size(self):        """Get size in bytes."""        pass        @abstractmethod    def display(self, indent=0):        """Display item."""        pass        def __str__(self):        return self.name class File(FileSystemItem):    """File class."""        def __init__(self, name, content=""):        super().__init__(name)        self._content = content        @property    def content(self):        """Get file content."""        return self._content        @content.setter    def content(self, value):        """Set file content."""        self._content = value        def get_size(self):        """Return content size."""        return len(self._content)        def display(self, indent=0):        """Display file."""        print("  " * indent + f"📄 {self.name} ({self.get_size()} bytes)") class Directory(FileSystemItem):    """Directory class."""        def __init__(self, name):        super().__init__(name)        self.children = []        def add(self, item):        """Add file or directory."""        self.children.append(item)        def remove(self, item_name):        """Remove file or directory."""        self.children = [c for c in self.children if c.name != item_name]        def get_size(self):        """Total size of all children."""        return sum(child.get_size() for child in self.children)        def display(self, indent=0):        """Display directory tree."""        print("  " * indent + f"📁 {self.name}/")        for child in self.children:            child.display(indent + 1)        def __len__(self):        """Number of items."""        return len(self.children)        def __getitem__(self, index):        """Get child by index."""        return self.children[index] # Usageroot = Directory("root") # Create structuredocs = Directory("documents")docs.add(File("resume.txt", "My resume content..."))docs.add(File("cover_letter.txt", "Dear hiring manager...")) images = Directory("images")images.add(File("photo1.jpg", "binary data" * 1000))images.add(File("photo2.jpg", "binary data" * 1500)) root.add(docs)root.add(images)root.add(File("readme.txt", "This is readme")) # Displayroot.display() print(f"\nTotal size: {root.get_size()} bytes")print(f"Items in images: {len(images)}")

5. Custom Collection with Magic Methods

class Playlist:    """Music playlist with magic methods."""        def __init__(self, name):        self.name = name        self.songs = []        def add(self, song):        """Add song."""        self.songs.append(song)        def __len__(self):        """Number of songs."""        return len(self.songs)        def __getitem__(self, index):        """Get song by index."""        return self.songs[index]        def __setitem__(self, index, song):        """Set song by index."""        self.songs[index] = song        def __contains__(self, song):        """Check if song in playlist."""        return song in self.songs        def __iter__(self):        """Make iterable."""        return iter(self.songs)        def __add__(self, other):        """Merge playlists."""        new_playlist = Playlist(f"{self.name} + {other.name}")        new_playlist.songs = self.songs + other.songs        return new_playlist        def __str__(self):        return f"Playlist: {self.name} ({len(self)} songs)"        def __repr__(self):        return f"Playlist('{self.name}', {len(self)} songs)" class Song:    """Song class."""        def __init__(self, title, artist, duration):        self.title = title        self.artist = artist        self.duration = duration  # seconds        def __str__(self):        mins = self.duration // 60        secs = self.duration % 60        return f"{self.title} - {self.artist} ({mins}:{secs:02d})"        def __eq__(self, other):        """Songs equal if same title and artist."""        return self.title == other.title and self.artist == other.artist # Usageplaylist1 = Playlist("Rock Classics")playlist1.add(Song("Bohemian Rhapsody", "Queen", 354))playlist1.add(Song("Stairway to Heaven", "Led Zeppelin", 482))playlist1.add(Song("Hotel California", "Eagles", 391)) playlist2 = Playlist("Pop Hits")playlist2.add(Song("Shape of You", "Ed Sheeran", 234))playlist2.add(Song("Blinding Lights", "The Weeknd", 200)) print(playlist1)print(playlist2) # Container methodsprint(f"\nSongs in {playlist1.name}: {len(playlist1)}")print(f"First song: {playlist1[0]}") # Check membershipsong = Song("Hotel California", "Eagles", 391)print(f"\n'{song.title}' in playlist: {song in playlist1}") # Iterateprint(f"\n{playlist1.name} tracklist:")for i, song in enumerate(playlist1, 1):    print(f"{i}. {song}") # Merge playlistsmixed = playlist1 + playlist2print(f"\n{mixed}")print(f"Total songs: {len(mixed)}")

Best Practices

# 1. Use ABC for abstract base classesfrom abc import ABC, abstractmethod class Animal(ABC):    @abstractmethod    def make_sound(self):        pass # 2. Call super().__init__() in child classesclass Dog(Animal):    def __init__(self, name):        super().__init__()        self.name = name # 3. Use @property for computed attributesclass Circle:    @property    def area(self):        return 3.14159 * self.radius ** 2 # 4. Implement __str__ and __repr__class Person:    def __str__(self):        return f"{self.name}"        def __repr__(self):        return f"Person('{self.name}', {self.age})" # 5. Use composition over inheritance when appropriateclass Car:    def __init__(self):        self.engine = Engine()  # Composition        self.wheels = [Wheel() for _ in range(4)]

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

Bài 1: Shape Hierarchy

Tạo hierarchy: ShapeRectangle, Circle, Triangle

  • Implement area()perimeter()
  • Add __str__ và comparison methods
  • Create list of shapes và sort by area

Bài 2: Account System

Tạo BankAccount và subclasses: SavingsAccount, CheckingAccount

  • Different interest rates
  • Transaction limits
  • Property decorators cho balance
  • Magic methods để compare accounts

Bài 3: Game Inventory

Tạo Item base class và subclasses: Weapon, Armor, Potion

  • Inventory class với magic methods
  • __len__, __getitem__, __contains__
  • Add/remove items
  • Calculate total value

Bài 4: Social Media

Tạo Post base class và: TextPost, ImagePost, VideoPost

  • User class với posts
  • Like/comment functionality
  • __add__ để merge user feeds
  • Property cho engagement rate

Bài 5: Task Management

Tạo Task base class và: TodoTask, RecurringTask, ProjectTask

  • TaskManager với filtering
  • Priority comparison
  • Status properties
  • Iterator pattern

Tóm Tắt

Inheritance: Class kế thừa từ parent class
super(): Gọi methods từ parent class
Polymorphism: Same interface, different implementations
Encapsulation: Private (__attr) và protected (_attr)
@property: Getter/setter cho attributes
Magic Methods: Customize object behavior

  • __str__, __repr__: String representation
  • __eq__, __lt__: Comparisons
  • __add__, __mul__: Arithmetic
  • __len__, __getitem__: Container behavior

Kết Thúc Module Python Cơ Bản

🎉 Chúc mừng! Bạn đã hoàn thành Module Python Cơ Bản với 15 bài học:

  1. Giới thiệu Python
  2. Variables & Data Types
  3. Operators
  4. Strings
  5. Lists
  6. Tuples
  7. Dictionaries
  8. Sets
  9. Control Flow
  10. Loops
  11. Functions
  12. Modules & Packages
  13. File I/O
  14. Error Handling
  15. Object-Oriented Programming

Bài Tiếp Theo

Bài 16: - Python Basics - Practices & Projects 🚀


Remember:

  • Inheritance = Code reusability
  • Polymorphism = Flexibility
  • Encapsulation = Data protection
  • Properties = Controlled access
  • Magic methods = Python magic! ✨