Câu 1Cách nào sau đây đúng để cho Dog kế thừa từ Animal?
Kế thừa class — Tái sử dụng tính năng từ class có sẵn
Học kế thừa class trong Python. Kế thừa thuộc tính và phương thức bằng class Child(Parent):, override phương thức, và gọi phiên bản của lớp cha với super() — kèm sơ đồ.
Lần trước ta dùng các phương thức đặc biệt như __add__ và __str__ để dạy các thao tác có sẵn cho class của mình. Lần này ta bao quát một trong những ý tưởng cốt lõi của OOP: kế thừa class.
Tại sao dùng kế thừa?
Khi xây dựng app, bạn thường gặp những class gần như giống nhau nhưng khác ở vài chỗ. Hãy tưởng tượng Customer, Employee, và Admin — tất cả đều giữ name và email rồi trả về một greeting(), nhưng mỗi class có thêm vài thuộc tính hoặc phương thức riêng.
Bạn có thể viết cùng name / email / greeting() ở cả ba class — nhưng code sẽ bị trùng lặp và mỗi thay đổi phải chạm vào ba chỗ. Cách gọn gàng là đặt phần dùng chung vào một lớp cha và để mỗi lớp con «kế thừa từ lớp cha và chỉ viết phần khác biệt». Đó là kế thừa (inheritance).
Person). Chỉ phần khác biệt mới vào lớp con (Employee). Không cần khai báo lại name hay greeting mỗi lần.Kế thừa từ lớp cha — class Child(Parent):
Cú pháp đơn giản: khi định nghĩa subclass, đặt tên lớp cha trong dấu ngoặc tròn sau tên class. Subclass tự động lấy hết thuộc tính và phương thức của lớp cha.
Dưới đây, Person là lớp cha (có name, age, và greeting) và Employee là lớp con. Employee không định nghĩa một phương thức nào của riêng nó, nhưng gọi greeting() trên nó vẫn chạy nhờ kế thừa.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greeting(self):
print(f"Xin chào, {self.name}")
def say_age(self):
print(f"{self.age} tuổi")
class Employee(Person): # kế thừa Person
pass # phần thân có thể để trống
e = Employee("Minh", 45)
e.greeting() # Xin chào, Minh <- phương thức của lớp cha
e.say_age() # 45 tuổi
- Thuộc tính: name / age
- Phương thức: greeting() / say_age()
- __init__(self, name, age)
- Phần thân để trống (chỉ pass)
- → Kế thừa thuộc tính và phương thức của lớp cha
- Employee("Minh", 45) gọi __init__ của lớp cha
Override phương thức — Thay thế bằng cùng tên
Chỉ kế thừa thì bạn nhận được hành vi của lớp cha y nguyên. Nhưng nếu bạn viết một phương thức cùng tên trong lớp con, bản của lớp con sẽ được ưu tiên. Đó là override (ghi đè).
Ví dụ, để cho Employee một lời chào khác như «Xin chào, nhân viên Minh», định nghĩa lại greeting trong lớp con. Gọi nó trên một instance Person cho lời chào của lớp cha; gọi trên một instance Employee cho lời chào của lớp con. Hành vi chuyển đổi dựa trên class mà instance đang gọi thuộc về.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greeting(self):
print(f"Xin chào, {self.name}")
class Employee(Person):
def greeting(self): # cùng tên = override
print(f"Xin chào, nhân viên {self.name}")
p = Person("Linh", 30)
e = Employee("Minh", 45)
p.greeting() # Xin chào, Linh
e.greeting() # Xin chào, nhân viên Minh <- bản của lớp con thắng
super() — Gọi phương thức của lớp cha
Đôi khi bạn muốn override một phương thức nhưng vẫn dựa trên hành vi của lớp cha. Đó là lúc dùng super(). Viết super().method() gọi phương thức cùng tên trên lớp cha của class hiện tại.
Ví dụ, giả sử bạn muốn «greeting của lớp cha (in Xin chào, Minh) theo sau là một dòng phụ ở lớp con». Trong greeting của lớp con, gọi super().greeting() trước rồi thêm dòng riêng của bạn.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greeting(self):
print(f"Xin chào, {self.name}")
class Employee(Person):
def greeting(self):
super().greeting() # ① chạy greeting của lớp cha trước
print(f"Tôi là nhân viên {self.name}") # ② rồi đến dòng riêng của lớp con
Employee("Minh", 45).greeting()
# Xin chào, Minh
# Tôi là nhân viên Minh
greeting của lớp con, gọi super().greeting() chạy greeting của lớp cha trực tiếp. self được truyền tự động — bạn không viết.Override __init__ để thêm thuộc tính
Nơi super() tỏa sáng nhất là khi bạn override __init__. Hãy tưởng tượng lớp con cần thuộc tính của lớp cha cộng thêm vài thuộc tính riêng — ví dụ, Employee cũng nên giữ phone_number.
Trong trường hợp đó bạn định nghĩa lại __init__ ở lớp con, nhưng việc khởi tạo của lớp cha (self.name = name v.v.) được xử lý gọn trong một dòng bằng super().__init__(name, age), và bạn thêm các thuộc tính phụ sau đó. Như vậy bạn không phải lặp lại các phép gán name / age.
Employee(...) chạy __init__ của lớp con → super().__init__(name, age) để lớp cha gán name / age → rồi lớp con thêm phone_number. Trạng thái cuối: thuộc tính lớp cha + lớp con đầy đủ.class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Employee(Person):
def __init__(self, name, age, phone_number):
super().__init__(name, age) # ① lớp cha set name / age
self.phone_number = phone_number # ② thêm thuộc tính riêng của lớp con
def show(self):
print(f"{self.name} / {self.age} / {self.phone_number}")
Employee("Minh", 45, "555-1111").show()
# Minh / 45 / 555-1111
Quên super().__init__(...) thì thuộc tính biến mất
Nếu bạn override __init__ nhưng quên gọi super().__init__(...), các phép gán của lớp cha như self.name = name không bao giờ chạy. Lần tới bạn đọc self.name, bạn sẽ nhận AttributeError. Mỗi khi override __init__ ở lớp con, gọi super().__init__(...) ngay đầu tiên — biến nó thành thói quen.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Mô tả nào chính xác nhất về override phương thức?
Câu 3Nếu bạn quên gọi super().__init__(...) trong __init__ của lớp con, điều gì xảy ra?