"self" được trình thông dịch gán cho đối tượng hiện tại khi một phương thức được gọi và trình thông dịch mong đợi đối số đầu tiên của mỗi phương thức sẽ tính đến điều này
Ghi đè là thay đổi hoặc thay thế một phương thức của lớp cha bằng một phương thức mới [có cùng tên] trong lớp con. Không cần cú pháp đặc biệt để làm điều này;
Hàm super[] trả về đối tượng như một thể hiện của lớp cha, cho phép chúng ta gọi trực tiếp phương thức cha
đa thừa kế. Một lớp con kế thừa từ nhiều hơn một lớp cha có thể truy cập chức năng từ cả hai lớp đó. Hình thức đa thừa kế đơn giản và hữu ích nhất được gọi là mixin. Một mixin nói chung là một siêu lớp không tồn tại riêng mà được kế thừa bởi một số lớp khác để cung cấp chức năng bổ sung
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
Cú pháp đa thừa kế trông giống như một danh sách tham số trong định nghĩa lớp. Thay vì bao gồm một lớp cơ sở bên trong dấu ngoặc đơn, chúng tôi bao gồm hai [hoặc nhiều hơn] được phân tách bằng dấu phẩy
>>>e = EmailableContact["Test user ", "testuser@example.net"]
>>>e.send_mail["hello, test email here"]
Sending mail to testuser@example.net
- Đa kế thừa hoạt động tốt khi trộn các phương thức từ các lớp khác nhau, nhưng sẽ rất lộn xộn khi chúng ta phải làm việc với các phương thức gọi trên lớp cha. Vì có nhiều lớp cha. Làm thế nào để chúng ta biết cái nào để gọi?
Vấn đề kim cương
- Nếu chúng ta có hai phương thức init gốc mà cả hai đều cần được khởi tạo và chúng cần được khởi tạo với các đối số khác nhau. làm sao chúng ta làm việc đó bây giờ?
class Addressholder: def __init__[self, street, city, state, code]: self.street = street self.city = city self.state = state self.code = code class Friend[Contact, Addressholder]: def __init__[self, name, email, phone, street, city, state, code]: Contact.__init__[self, name, email] AddressHolder.__init__[self, street, city, state, code] self.phone = phone
Trong ví dụ này, chúng ta gọi trực tiếp hàm init trên mỗi lớp cha và chuyển đối số self một cách rõ ràng. Ví dụ này hoạt động về mặt kỹ thuật; chúng ta có thể truy cập các biến khác nhau trực tiếp trên lớp. Nhưng có một vài vấn đề
Có khả năng một siêu lớp không được khởi tạo nếu chúng ta bỏ qua việc gọi trình khởi tạo một cách rõ ràng
Khả năng siêu lớp được gọi nhiều lần, do tổ chức của hệ thống phân cấp lớp. Sơ đồ thừa kế bên dưới
Phương thức init từ lớp Friend trước tiên gọi init trên Contact để khởi tạo hoàn toàn siêu lớp đối tượng [tất cả các lớp bắt nguồn từ obejct]. Sau đó, Friend gọi lệnh init trên AddressHolder, lệnh này ngầm khởi tạo lại siêu lớp đối tượng. Lớp cha đã được thiết lập hai lần. Hãy tưởng tượng cố gắng kết nối với cơ sở dữ liệu hai lần cho mọi yêu cầu. Lớp cơ sở chỉ nên được gọi một lần
- Về mặt kỹ thuật, thứ tự mà các phương thức có thể được gọi có thể được điều chỉnh nhanh chóng bằng cách sửa đổi thuộc tính mro [Thứ tự phân giải phương thức] của lớp
Hãy xem xét một ví dụ thứ hai minh họa vấn đề này rõ ràng hơn. Ở đây có một lớp cơ sở có một phương thức tên là call_me. Hai lớp con ghi đè phương thức đó và sau đó một lớp con khác mở rộng cả hai lớp này bằng cách sử dụng đa kế thừa. Điều này được gọi là kế thừa kim cương vì hình dạng kim cương của sơ đồ lớp
Kim cương là thứ khiến cho việc thừa kế trở nên khó khăn. Về mặt kỹ thuật, tất cả đa kế thừa trong Python3 là kế thừa kim cương, bởi vì tất cả các lớp đều kế thừa từ đối tượng
class MyClass: @classmethod #>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net17 và
class SavingsAccount[BankAccount]: def __init__[self, balance, interest_rate]: BankAccount.__init__[self, balance] self.interest_rate = interest_rate # new functionality def compute_interest[self, n_periods=1]: return self.balance * [[1 + self.interest_rate] ** n_periods - 1]6 theo quy ước. Trả về một boolean
class MyClass: @classmethod #>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net74 sẽ trả về True, mặc dù chúng tôi đang so sánh số điện thoại với số tài khoản ngân hàng. Bạn nên kiểm tra lớp đối tượng được truyền cho phương thức eq[] để đảm bảo việc so sánh có ý nghĩa
So sánh và kế thừa
- Điều gì xảy ra khi một đối tượng được so sánh với một đối tượng của lớp con?
class MyClass: @classmethod #>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net10
- Chúng ta phải luôn định nghĩa ít nhất một trong các phương thức biểu diễn chuỗi cho đối tượng out để đảm bảo rằng người sử dụng lớp của chúng ta có thể lấy thông tin quan trọng về đối tượng một cách dễ dàng
ngoại lệ
xử lý ngoại lệ
- sử dụng mã
class MyClass: @classmethod #>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net
12Bắt ngoại lệ tùy chỉnh
13>>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net
Xử lý phân cấp ngoại lệ
- Tốt hơn là liệt kê các khối ngoại trừ theo thứ tự cụ thể tăng dần, tôi. e. con trước cha mẹ, nếu không thì ngoại lệ con sẽ được gọi trong khối cha ngoại trừ
Thiết kế cho sự kế thừa và đa hình
đa hình
- Sử dụng giao diện thống nhất để thao tác trên các đối tượng thuộc các lớp khác nhau
- Lớp BankAccount, SavingsAccount vàCheckingAccount có phương thức rút tiền nhưng phương thức ChecingAccount đang thực thi mã khác
Tất cả những gì quan trọng là một giao diện
- Giả sử chúng ta định nghĩa một chức năng để rút cùng một lượng tiền từ toàn bộ danh sách tài khoản cùng một lúc. Hàm không quan tâm liệu các đối tượng được truyền cho nó làCheckingAccount, SavingsAccount hay hỗn hợp. Tất cả vấn đề là họ có một phương thức rút tiền chấp nhận một đối số đủ để hàm hoạt động
- Nó không kiểm tra việc rút tiền nào nên gọi là bản gốc hay bản sửa đổi. Khi phương thức rút tiền thực sự được gọi là python sẽ tự động kéo đúng phương thức
- Rút tiền đã sửa đổi khi Tài khoản kiểm tra đang được xử lý và phương thức cơ bản khi Tài khoản tiết kiệm hoặc tài khoản thông thường được gọi
14>>>e = EmailableContact["Test user ", "testuser@example.net"] >>>e.send_mail["hello, test email here"] Sending mail to testuser@example.net
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
2 không cần kiểm tra đối tượng để biết phải gọi# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
3 nào
Nguyên tắc thay thế Liskov
- Có một nguyên tắc thiết kế hướng đối tượng cơ bản về thời điểm và cách sử dụng tính kế thừa đúng cách được gọi là nguyên tắc thay thế Liskov. `Lớp cơ sở có thể hoán đổi với bất kỳ lớp con nào của nó mà không làm thay đổi bất kỳ thuộc tính nào của chương trình
- Điều đó có nghĩa là bất cứ nơi nào
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
4 hoạt động, thì# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
5 cũng sẽ hoạt động. cho e. g Hàm# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
6 hoạt động bất kể loại tài khoản nào được sử dụng
LSP vi phạm
- không tương thích cú pháp.
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
7 yêu cầu 1 tham số, nhưng# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
8 yêu cầu 2 - Phân lớp tăng cường điều kiện đầu vào.
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
7 chấp nhận bất kỳ số lượng nào, nhưng# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
8 cho rằng số lượng đó bị hạn chế - Phân lớp làm suy yếu điều kiện đầu ra.
# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
7 chỉ có thể để lại số dư dương hoặc gây ra lỗi,# Example class Contact: #all_contacts = [] all_contacts = ContactList[] def __init__[self, name, email]: self.name = name self.email = email #Contact.all_contacts.append[self] self.all_contacts.append[self] class MailSender: def send_mail[self, message]: print["sending mail to " + self.email] # multiple inheritance class EmailableContact[Contact, MailSender]: pass
8 có thể để lại số dư âm - Thay đổi các thuộc tính bổ sung trong phương thức của lớp con không được thay đổi trong lớp cơ sở
- Ném các ngoại lệ bổ sung trong phương thức của lớp con mà lớp cơ sở không ném
Không LSP - Không kế thừa
Nếu hệ thống phân cấp lớp làm hỏng LSP, thì chúng ta không nên sử dụng tính kế thừa bcz, nó có khả năng hoạt động mã theo những cách không thể đoán trước