Hướng dẫn composition python - thành phần trăn

Qua các series tự học về Design Pattern, Hôm nay cafedevn chia sẻ cho ace ví dụ và code cụ thể về cách sử dụng Composite design pattern với ngôn ngữ lập trình Python. Nhằm giúp ace hiểu rõ cách sử Pattern này với Python một cách nhanh nhất và áp dụng nó vào thực tế.

Show

Nội dung chính

  • Phần code
  • What Are Inheritance and Composition?
  • What’s Inheritance?
  • What’s Composition?
  • An Overview of Inheritance in Python
  • The Object Super Class
  • Exceptions Are an Exception
  • Creating Class Hierarchies
  • Abstract Base Classes in Python
  • Implementation Inheritance vs Interface Inheritance
  • The Class Explosion Problem
  • Inheriting Multiple Classes
  • Composition in Python
  • Flexible Designs With Composition
  • Customizing Behavior With Composition
  • Choosing Between Inheritance and Composition in Python
  • Inheritance to Model “Is A” Relationship
  • Mixing Features With Mixin Classes
  • Composition to Model “Has A” Relationship
  • Composition to Change Run-Time Behavior
  • Choosing Between Inheritance and Composition in Python
  • Inheritance to Model “Is A” Relationship

Mixing Features With Mixin Classes

Phần code

"""
Compose objects into tree structures to represent part-whole
hierarchies. Composite lets clients treat individual objects and
compositions of objects uniformly.
"""

import abc


class Component(metaclass=abc.ABCMeta):
    """
    Declare the interface for objects in the composition.
    Implement default behavior for the interface common to all classes,
    as appropriate.
    Declare an interface for accessing and managing its child
    components.
    Define an interface for accessing a component's parent in the
    recursive structure, and implement it if that's appropriate
    (optional).
    """

    @abc.abstractmethod
    def operation(self):
        pass


class Composite(Component):
    """
    Define behavior for components having children.
    Store child components.
    Implement child-related operations in the Component interface.
    """

    def __init__(self):
        self._children = set()

    def operation(self):
        for child in self._children:
            child.operation()

    def add(self, component):
        self._children.add(component)

    def remove(self, component):
        self._children.discard(component)


class Leaf(Component):
    """
    Represent leaf objects in the composition. A leaf has no children.
    Define behavior for primitive objects in the composition.
    """

    def operation(self):
        pass


def main():
    leaf = Leaf()
    composite = Composite()
    composite.add(leaf)
    composite.operation()


if __name__ == "__main__":
    main()

Composition to Model “Has A” Relationshipmọi lúc mọi nơi tại đây.

Composition to Change Run-Time Behavior

  • Recommended Reading
  • Soạn các đối tượng thành cấu trúc cây để thể hiện một phần toàn bộ hệ thống phân cấp. Composite cho phép khách hàng xử lý các đối tượng riêng lẻ và thành phần của các đối tượng một cách thống nhất.cafedev tại đây

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

  • Tài liệu từ cafedev:
  • Full series tự học Design Pattern từ cơ bản tới nâng cao tại đây nha.
  • Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây
  • Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
  • Group Facebook
  • Fanpage
  • Youtube
  • Instagram

Twitter

Linkedin

Pinterest This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Inheritance and Composition: A Python OOP Guide

Trang chủinheritance and composition in Python. Inheritance and composition are two important concepts in object oriented programming that model the relationship between two classes. They are the building blocks of object oriented design, and they help programmers to write reusable code.

Chào thân ái và quyết thắng!:

  • Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!
  • Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Inheritance and Composition: A Python OOP Guide
  • In this article, you’ll explore inheritance and composition in Python. Inheritance and composition are two important concepts in object oriented programming that model the relationship between two classes. They are the building blocks of object oriented design, and they help programmers to write reusable code.
  • By the end of this article, you’ll know how to:
  • Use inheritance in Python
  • Model class hierarchies using inheritance

What Are Inheritance and Composition?

Use multiple inheritance in Python and understand its drawbacks and composition are two major concepts in object oriented programming that model the relationship between two classes. They drive the design of an application and determine how the application should evolve as new features are added or requirements change.

Use composition to create complex objects

What’s Inheritance?

Reuse existing code by applying composition models what is called an is a relationship. This means that when you have a

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7 class that inherits from a
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8 class, you created a relationship where
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7 is a specialized version of
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8.

Change application behavior at run-time through composition

Hướng dẫn composition python - thành phần trăn

Inheritance and composition are two major concepts in object oriented programming that model the relationship between two classes. They drive the design of an application and determine how the application should evolve as new features are added or requirements change.extends is usually added to the arrow.

Both of them enable code reuse, but they do it in different ways.is an

# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
1. This means that
# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
2 inherits the interface and implementation of
# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
1, and
# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
2 objects can be used to replace
# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
1 objects in the application.

Inheritance models what is called an is a relationship. This means that when you have a

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7 class that inherits from a
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8 class, you created a relationship where
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7 is a specialized version of
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8.

Inheritance is represented using the Unified Modeling Language or UML in the following way:

What’s Composition?

Classes are represented as boxes with the class name on top. The inheritance relationship is represented by an arrow from the derived class pointing to the base class. The word extends is usually added to the arrow. is a concept that models a has a relationship. It enables creating complex types by combining objects of other types. This means that a class

>>> class MyClass:
...     pass
...
03 can contain an object of another class
>>> class MyClass:
...     pass
...
04. This relationship means that a
>>> class MyClass:
...     pass
...
03 has a
>>> class MyClass:
...     pass
...
04.

UML đại diện cho bố cục như sau:

Thành phần được thể hiện thông qua một dòng với một viên kim cương ở lớp tổng hợp chỉ vào lớp thành phần. Phía tổng hợp có thể thể hiện tính chất của mối quan hệ. Cardinality cho biết số lượng hoặc phạm vi hợp lệ của các trường hợp ____104 mà lớp

>>> class MyClass:
...     pass
...
03 sẽ chứa.

Trong sơ đồ trên,

>>> class MyClass:
...     pass
...
09 thể hiện rằng lớp
>>> class MyClass:
...     pass
...
03 chứa một đối tượng loại
>>> class MyClass:
...     pass
...
04. Cardinality có thể được thể hiện theo những cách sau:

  • Một số cho biết số lượng các trường hợp
    >>> class MyClass:
    ...     pass
    ...
    
    04 được chứa trong
    >>> class MyClass:
    ...     pass
    ...
    
    03.
    indicates the number of
    >>> class MyClass:
    ...     pass
    ...
    
    04 instances that are contained in the
    >>> class MyClass:
    ...     pass
    ...
    
    03.
  • Biểu tượng * chỉ ra rằng lớp
    >>> class MyClass:
    ...     pass
    ...
    
    03 có thể chứa một số lượng thay đổi các phiên bản ____104.
    indicates that the
    >>> class MyClass:
    ...     pass
    ...
    
    03 class can contain a variable number of
    >>> class MyClass:
    ...     pass
    ...
    
    04 instances.
  • Phạm vi 1..4 chỉ ra rằng lớp
    >>> class MyClass:
    ...     pass
    ...
    
    03 có thể chứa một phạm vi của các trường hợp ____104. Phạm vi được chỉ định với số lượng trường hợp tối thiểu và tối đa, hoặc tối thiểu và nhiều trường hợp như trong 1 ..*.
    indicates that the
    >>> class MyClass:
    ...     pass
    ...
    
    03 class can contain a range of
    >>> class MyClass:
    ...     pass
    ...
    
    04 instances. The range is indicated with the minimum and maximum number of instances, or minimum and many instances like in 1..*.

Ví dụ: lớp

# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
2 của bạn có thể được sáng tác bởi một đối tượng khác của loại
>>> class MyClass:
...     pass
...
19. Thành phần cho phép bạn thể hiện mối quan hệ đó bằng cách nói
# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
2 có
>>> class MyClass:
...     pass
...
19.has a
>>> class MyClass:
...     pass
...
19.

Thành phần cho phép bạn sử dụng lại mã bằng cách thêm các đối tượng vào các đối tượng khác, trái ngược với việc kế thừa giao diện và triển khai các lớp khác. Cả hai lớp

# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate
2 và
>>> class MyClass:
...     pass
...
23 đều có thể tận dụng chức năng của
>>> class MyClass:
...     pass
...
19 thông qua thành phần mà không cần lấy một lớp từ lớp kia.

Tổng quan về thừa kế trong Python

Tất cả mọi thứ trong Python là một đối tượng. Các mô -đun là đối tượng, định nghĩa lớp và chức năng là đối tượng và tất nhiên, các đối tượng được tạo từ các lớp cũng là đối tượng.

Kế thừa là một tính năng cần thiết của mọi ngôn ngữ lập trình hướng đối tượng. Điều này có nghĩa là Python hỗ trợ thừa kế và như bạn sẽ thấy sau đó, nó là một trong số ít ngôn ngữ hỗ trợ nhiều kế thừa.

Khi bạn viết mã Python bằng các lớp, bạn đang sử dụng kế thừa ngay cả khi bạn không biết bạn sử dụng nó. Hãy cùng xem xét điều đó có nghĩa là gì.

Đối tượng siêu lớp

Cách dễ nhất để thấy sự kế thừa trong Python là nhảy vào vỏ tương tác Python và viết một chút mã. Bạn sẽ bắt đầu bằng cách viết lớp đơn giản nhất có thể:

>>>

>>> class MyClass:
...     pass
...

Bạn đã tuyên bố một lớp

>>> class MyClass:
...     pass
...
25 không làm được gì nhiều, nhưng nó sẽ minh họa các khái niệm kế thừa cơ bản nhất. Bây giờ bạn đã khai báo lớp, bạn có thể sử dụng chức năng
>>> class MyClass:
...     pass
...
26 để liệt kê các thành viên của nó:

>>>

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']

Bạn đã tuyên bố một lớp

>>> class MyClass:
...     pass
...
25 không làm được gì nhiều, nhưng nó sẽ minh họa các khái niệm kế thừa cơ bản nhất. Bây giờ bạn đã khai báo lớp, bạn có thể sử dụng chức năng
>>> class MyClass:
...     pass
...
26 để liệt kê các thành viên của nó:

>>>

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']

Bạn đã tuyên bố một lớp

>>> class MyClass:
...     pass
...
25 không làm được gì nhiều, nhưng nó sẽ minh họa các khái niệm kế thừa cơ bản nhất. Bây giờ bạn đã khai báo lớp, bạn có thể sử dụng chức năng
>>> class MyClass:
...     pass
...
26 để liệt kê các thành viên của nó:

>>> class MyClass:
...     pass
...
26 Trả về danh sách tất cả các thành viên trong đối tượng được chỉ định. Bạn đã không tuyên bố bất kỳ thành viên nào trong
>>> class MyClass:
...     pass
...
25, vậy danh sách đến từ đâu? Bạn có thể tìm hiểu bằng cách sử dụng trình thông dịch tương tác:

Như bạn có thể thấy, hai danh sách gần như giống hệt nhau. Có một số thành viên bổ sung trong >>> class MyClass: ... pass ... 25 như >>> class MyClass: ... pass ... 30 và >>> class MyClass: ... pass ... 31, nhưng mỗi thành viên của lớp >>> class MyClass: ... pass ... 32 cũng có mặt trong >>> class MyClass: ... pass ... 25.

Điều này là do mỗi lớp bạn tạo ra trong Python hoàn toàn xuất phát từ

>>> class MyClass:
...     pass
...
32. Bạn có thể rõ ràng hơn và viết
>>> class MyClass:
...     pass
...
35, nhưng nó dự phòng và không cần thiết.

Ngoại lệ là một ngoại lệ

>>>

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException

Bạn đã tuyên bố một lớp

>>> class MyClass:
...     pass
...
25 không làm được gì nhiều, nhưng nó sẽ minh họa các khái niệm kế thừa cơ bản nhất. Bây giờ bạn đã khai báo lớp, bạn có thể sử dụng chức năng
>>> class MyClass:
...     pass
...
26 để liệt kê các thành viên của nó:

>>> class MyClass:
...     pass
...
26 Trả về danh sách tất cả các thành viên trong đối tượng được chỉ định. Bạn đã không tuyên bố bất kỳ thành viên nào trong
>>> class MyClass:
...     pass
...
25, vậy danh sách đến từ đâu? Bạn có thể tìm hiểu bằng cách sử dụng trình thông dịch tương tác:

Như bạn có thể thấy, hai danh sách gần như giống hệt nhau. Có một số thành viên bổ sung trong

>>> class MyClass:
...     pass
...
25 như
>>> class MyClass:
...     pass
...
30 và
>>> class MyClass:
...     pass
...
31, nhưng mỗi thành viên của lớp
>>> class MyClass:
...     pass
...
32 cũng có mặt trong
>>> class MyClass:
...     pass
...
25.

>>>

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError

Bạn đã tuyên bố một lớp

>>> class MyClass:
...     pass
...
25 không làm được gì nhiều, nhưng nó sẽ minh họa các khái niệm kế thừa cơ bản nhất. Bây giờ bạn đã khai báo lớp, bạn có thể sử dụng chức năng
>>> class MyClass:
...     pass
...
26 để liệt kê các thành viên của nó:

>>> class MyClass: ... pass ... 26 Trả về danh sách tất cả các thành viên trong đối tượng được chỉ định. Bạn đã không tuyên bố bất kỳ thành viên nào trong >>> class MyClass: ... pass ... 25, vậy danh sách đến từ đâu? Bạn có thể tìm hiểu bằng cách sử dụng trình thông dịch tương tác:

Như bạn có thể thấy, hai danh sách gần như giống hệt nhau. Có một số thành viên bổ sung trong

>>> class MyClass:
...     pass
...
25 như
>>> class MyClass:
...     pass
...
30 và
>>> class MyClass:
...     pass
...
31, nhưng mỗi thành viên của lớp
>>> class MyClass:
...     pass
...
32 cũng có mặt trong
>>> class MyClass:
...     pass
...
25.

Điều này là do mỗi lớp bạn tạo ra trong Python hoàn toàn xuất phát từ

>>> class MyClass:
...     pass
...
32. Bạn có thể rõ ràng hơn và viết
>>> class MyClass:
...     pass
...
35, nhưng nó dự phòng và không cần thiết.

Ngoại lệ là một ngoại lệ

Bạn bắt đầu bằng cách thực hiện một lớp

>>> class MyClass:
...     pass
...
45 xử lý bảng lương:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')

>>> class MyClass:
...     pass
...
45 thực hiện phương pháp
>>> class MyClass:
...     pass
...
47 lấy bộ sưu tập nhân viên và in
>>> class MyClass:
...     pass
...
48,
>>> class MyClass:
...     pass
...
49 và kiểm tra số tiền bằng phương pháp
>>> class MyClass:
...     pass
...
47 được phơi sáng trên mỗi đối tượng nhân viên.

Bây giờ, bạn thực hiện một lớp cơ sở

>>> class MyClass:
...     pass
...
51 xử lý giao diện chung cho mọi loại nhân viên:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name

>>> class MyClass:
...     pass
...
51 là lớp cơ sở cho tất cả các loại nhân viên. Nó được xây dựng với một
>>> class MyClass:
...     pass
...
48 và
>>> class MyClass:
...     pass
...
49. Những gì bạn đang nói là mỗi
>>> class MyClass:
...     pass
...
51 phải có
>>> class MyClass:
...     pass
...
48 được gán cũng như một tên.

Hệ thống nhân sự yêu cầu mỗi

>>> class MyClass:
...     pass
...
51 được xử lý phải cung cấp giao diện
>>> class MyClass:
...     pass
...
47 trả lại mức lương hàng tuần cho nhân viên. Việc thực hiện giao diện đó khác nhau tùy thuộc vào loại
>>> class MyClass:
...     pass
...
51.

Ví dụ, nhân viên hành chính có mức lương cố định, vì vậy mỗi tuần họ được trả cùng một số tiền:

________số 8

Bạn tạo một lớp dẫn xuất

>>> class MyClass:
...     pass
...
60 kế thừa
>>> class MyClass:
...     pass
...
51. Lớp được khởi tạo với
>>> class MyClass:
...     pass
...
48 và
>>> class MyClass:
...     pass
...
49 theo yêu cầu của lớp cơ sở và bạn sử dụng
>>> class MyClass:
...     pass
...
64 để khởi tạo các thành viên của lớp cơ sở. Bạn có thể đọc tất cả về
>>> class MyClass:
...     pass
...
64 trong Supercharge Các lớp của bạn với Python Super ().

>>> class MyClass:
...     pass
...
60 cũng yêu cầu tham số khởi tạo
>>> class MyClass:
...     pass
...
67 đại diện cho số tiền mà nhân viên thực hiện mỗi tuần.

Lớp cung cấp phương pháp

>>> class MyClass:
...     pass
...
47 cần thiết được sử dụng bởi hệ thống nhân sự. Việc triển khai chỉ trả về số tiền được lưu trữ trong
>>> class MyClass:
...     pass
...
67.

Công ty cũng sử dụng nhân viên sản xuất được trả theo giờ, vì vậy bạn thêm

>>> class MyClass:
...     pass
...
70 vào hệ thống nhân sự:

# In hr.py

class HourlyEmployee(Employee):
    def __init__(self, id, name, hours_worked, hour_rate):
        super().__init__(id, name)
        self.hours_worked = hours_worked
        self.hour_rate = hour_rate

    def calculate_payroll(self):
        return self.hours_worked * self.hour_rate

Lớp

>>> class MyClass:
...     pass
...
70 được khởi tạo với
>>> class MyClass:
...     pass
...
48 và
>>> class MyClass:
...     pass
...
49, như lớp cơ sở, cộng với
>>> class MyClass:
...     pass
...
74 và
>>> class MyClass:
...     pass
...
75 cần thiết để tính toán bảng lương. Phương pháp
>>> class MyClass:
...     pass
...
47 được thực hiện bằng cách trả lại thời gian làm việc theo tỷ lệ giờ.

Cuối cùng, công ty sử dụng các cộng sự bán hàng được thanh toán thông qua mức lương cố định cộng với một khoản hoa hồng dựa trên doanh số của họ, vì vậy bạn tạo một lớp

>>> class MyClass:
...     pass
...
77:

>>> class MyClass:
...     pass
...
0

Bạn lấy

>>> class MyClass:
...     pass
...
77 từ
>>> class MyClass:
...     pass
...
60 vì cả hai lớp đều có
>>> class MyClass:
...     pass
...
67 để xem xét. Đồng thời,
>>> class MyClass:
...     pass
...
77 được khởi tạo với giá trị
>>> class MyClass:
...     pass
...
82 dựa trên doanh số cho nhân viên.

>>> class MyClass:
...     pass
...
47 tận dụng việc thực hiện lớp cơ sở để lấy tiền lương
>>> class MyClass:
...     pass
...
84 và thêm giá trị hoa hồng.

>>> class MyClass:
...     pass
...
77 xuất phát từ
>>> class MyClass:
...     pass
...
60, bạn có quyền truy cập trực tiếp vào thuộc tính
>>> class MyClass:
...     pass
...
67 và bạn có thể triển khai
>>> class MyClass:
...     pass
...
47 bằng cách sử dụng giá trị của thuộc tính đó.

Vấn đề với việc truy cập trực tiếp vào tài sản là nếu việc thực hiện

>>> class MyClass:
...     pass
...
89 thay đổi, thì bạn cũng sẽ phải thay đổi việc thực hiện
>>> class MyClass:
...     pass
...
90. Nó tốt hơn để dựa vào phương thức đã được triển khai trong lớp cơ sở và mở rộng chức năng khi cần thiết.

Bạn đã tạo hệ thống phân cấp hạng nhất của bạn cho hệ thống. Sơ đồ UML của các lớp trông như thế này:

Sơ đồ cho thấy hệ thống phân cấp kế thừa của các lớp. Các lớp dẫn xuất thực hiện giao diện

>>> class MyClass:
...     pass
...
91, được yêu cầu bởi
>>> class MyClass:
...     pass
...
45. Việc triển khai
>>> class MyClass:
...     pass
...
93 yêu cầu các đối tượng
>>> class MyClass:
...     pass
...
94 được thông qua có chứa một triển khai
>>> class MyClass:
...     pass
...
48,
>>> class MyClass:
...     pass
...
49 và
>>> class MyClass:
...     pass
...
97.

Các giao diện được biểu diễn tương tự như các lớp có giao diện từ phía trên tên giao diện. Tên giao diện thường được tiền tố với vốn

>>> class MyClass:
...     pass
...
98.interface above the interface name. Interface names are usually prefixed with a capital
>>> class MyClass:
...     pass
...
98.

Ứng dụng tạo ra nhân viên của mình và chuyển họ đến hệ thống bảng lương để xử lý bảng lương:

>>> class MyClass:
...     pass
...
1

Bạn có thể chạy chương trình trong dòng lệnh và xem kết quả:

>>> class MyClass:
...     pass
...
2

Chương trình tạo ra ba đối tượng nhân viên, một cho mỗi lớp dẫn xuất. Sau đó, nó tạo ra hệ thống bảng lương và chuyển danh sách nhân viên cho phương thức

>>> class MyClass:
...     pass
...
47 của nó, tính toán bảng lương cho mỗi nhân viên và in kết quả.

Lưu ý cách lớp cơ sở

>>> class MyClass:
...     pass
...
51 không xác định phương thức
>>> class MyClass:
...     pass
...
47. Điều này có nghĩa là nếu bạn đã tạo một đối tượng
>>> class MyClass:
...     pass
...
51 đơn giản và chuyển nó đến
>>> class MyClass:
...     pass
...
45, thì bạn sẽ gặp lỗi. Bạn có thể thử nó trong trình thông dịch tương tác Python:

>>>

>>> class MyClass:
...     pass
...
3

Mặc dù bạn có thể khởi tạo một đối tượng

>>> class MyClass:
...     pass
...
51, đối tượng có thể được sử dụng bởi
>>> class MyClass:
...     pass
...
45. Tại sao? Bởi vì nó có thể
>>> class MyClass:
...     pass
...
47 cho một
>>> class MyClass:
...     pass
...
51. Để đáp ứng các yêu cầu của
>>> class MyClass:
...     pass
...
45, bạn sẽ muốn chuyển đổi lớp
>>> class MyClass:
...     pass
...
51, hiện là một lớp cụ thể, thành một lớp trừu tượng. Bằng cách đó, không có nhân viên nào chỉ là một
>>> class MyClass:
...     pass
...
51, nhưng một trong đó thực hiện
>>> class MyClass:
...     pass
...
47.

Các lớp học cơ sở trừu tượng trong Python

Lớp

>>> class MyClass:
...     pass
...
51 trong ví dụ trên là cái được gọi là lớp cơ sở trừu tượng. Các lớp cơ sở trừu tượng tồn tại để được kế thừa, nhưng không bao giờ khởi tạo. Python cung cấp mô -đun
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
13 để xác định các lớp cơ sở trừu tượng.

Bạn có thể sử dụng các dấu gạch dưới hàng đầu trong tên lớp của mình để truyền đạt rằng các đối tượng của lớp đó không nên được tạo. Undercores cung cấp một cách thân thiện để ngăn chặn việc lạm dụng mã của bạn, nhưng họ không ngăn cản người dùng háo hức tạo ra các trường hợp của lớp đó.

Mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
13 trong Thư viện tiêu chuẩn Python cung cấp chức năng để ngăn chặn việc tạo các đối tượng từ các lớp cơ sở trừu tượng.

Bạn có thể sửa đổi việc triển khai lớp

>>> class MyClass:
...     pass
...
51 để đảm bảo rằng nó có thể được khởi tạo:

>>> class MyClass:
...     pass
...
4

Bạn lấy

>>> class MyClass:
...     pass
...
51 từ
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
17, làm cho nó trở thành một lớp cơ sở trừu tượng. Sau đó, bạn trang trí phương pháp
>>> class MyClass:
...     pass
...
47 với bộ trang trí
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
19.

Thay đổi này có hai tác dụng phụ đẹp:

  1. Bạn có thể nói với người dùng mô -đun rằng các đối tượng loại
    >>> class MyClass:
    ...     pass
    ...
    
    51 có thể được tạo ra.
  2. Bạn có thể nói với các nhà phát triển khác làm việc trên mô -đun
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    21 rằng nếu họ lấy từ
    >>> class MyClass:
    ...     pass
    ...
    
    51, thì họ phải ghi đè phương pháp trừu tượng
    >>> class MyClass:
    ...     pass
    ...
    
    47.

Bạn có thể thấy rằng các đối tượng thuộc loại

>>> class MyClass:
...     pass
...
51 có thể được tạo bằng cách sử dụng trình thông dịch tương tác:

>>>

>>> class MyClass:
...     pass
...
5

Đầu ra cho thấy lớp không thể được khởi tạo vì nó chứa một phương pháp trừu tượng

>>> class MyClass:
...     pass
...
97. Các lớp dẫn xuất phải ghi đè phương thức để cho phép tạo các đối tượng thuộc loại của chúng.

Kế thừa thực hiện so với kế thừa giao diện

Khi bạn lấy một lớp này từ một lớp khác, lớp dẫn xuất kế thừa cả hai:

  1. Giao diện lớp cơ sở: lớp dẫn xuất kế thừa tất cả các phương thức, thuộc tính và thuộc tính của lớp cơ sở. The derived class inherits all the methods, properties, and attributes of the base class.

  2. Việc triển khai lớp cơ sở: Lớp dẫn xuất kế thừa mã thực hiện giao diện lớp. The derived class inherits the code that implements the class interface.

Hầu hết thời gian, bạn sẽ muốn kế thừa việc triển khai một lớp, nhưng bạn sẽ muốn thực hiện nhiều giao diện, vì vậy các đối tượng của bạn có thể được sử dụng trong các tình huống khác nhau.

Ngôn ngữ lập trình hiện đại được thiết kế với khái niệm cơ bản này trong tâm trí. Chúng cho phép bạn kế thừa từ một lớp duy nhất, nhưng bạn có thể triển khai nhiều giao diện.

Trong Python, bạn không phải tuyên bố rõ ràng giao diện. Bất kỳ đối tượng nào thực hiện giao diện mong muốn có thể được sử dụng thay cho một đối tượng khác. Điều này được gọi là gõ vịt. Gõ vịt thường được giải thích là nếu nó hoạt động như một con vịt, thì nó là một con vịt.duck typing. Duck typing is usually explained as “if it behaves like a duck, then it’s a duck.”

Để minh họa điều này, bây giờ bạn sẽ thêm một lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
26 vào ví dụ trên mà không có nguồn gốc từ
>>> class MyClass:
...     pass
...
51:

>>> class MyClass:
...     pass
...
6

Lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
26 không có nguồn gốc từ
>>> class MyClass:
...     pass
...
51, nhưng nó phơi bày cùng một giao diện theo yêu cầu của
>>> class MyClass:
...     pass
...
45.
>>> class MyClass:
...     pass
...
93 yêu cầu một danh sách các đối tượng thực hiện giao diện sau:

  • Thuộc tính hoặc thuộc tính
    >>> class MyClass:
    ...     pass
    ...
    
    48 trả về ID nhân viên
    >>> class MyClass:
    ...     pass
    ...
    
    48
    property or attribute that returns the employee’s id
  • Thuộc tính hoặc thuộc tính
    >>> class MyClass:
    ...     pass
    ...
    
    49 đại diện cho tên nhân viên
    >>> class MyClass:
    ...     pass
    ...
    
    49
    property or attribute that represents the employee’s name
  • Một phương thức
    >>> class MyClass:
    ...     pass
    ...
    
    47 không lấy bất kỳ tham số nào và trả về số tiền trả lương để xử lý
    >>> class MyClass:
    ...     pass
    ...
    
    47
    method that doesn’t take any parameters and returns the payroll amount to process

Tất cả các yêu cầu này được đáp ứng bởi lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
26, vì vậy
>>> class MyClass:
...     pass
...
45 vẫn có thể tính toán bảng lương của nó.

Bạn có thể sửa đổi chương trình để sử dụng lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
26:

>>> class MyClass:
...     pass
...
7

Chương trình tạo ra một đối tượng

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
26 và thêm nó vào danh sách được xử lý bởi
>>> class MyClass:
...     pass
...
45. Bây giờ bạn có thể chạy chương trình và xem đầu ra của nó:

>>> class MyClass:
...     pass
...
8

Như bạn có thể thấy,

>>> class MyClass:
...     pass
...
45 vẫn có thể xử lý đối tượng mới vì nó đáp ứng giao diện mong muốn.

Vì bạn không phải xuất phát từ một lớp cụ thể để các đối tượng của bạn được chương trình có thể sử dụng lại, bạn có thể hỏi tại sao bạn nên sử dụng kế thừa thay vì chỉ triển khai giao diện mong muốn. Các quy tắc sau có thể giúp bạn:

  • Sử dụng kế thừa để sử dụng lại một triển khai: Các lớp dẫn xuất của bạn nên tận dụng hầu hết việc thực hiện lớp cơ sở của họ. Họ cũng phải mô hình hóa A là một mối quan hệ. Một lớp

    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    41 cũng có thể có
    >>> class MyClass:
    ...     pass
    ...
    
    48 và
    >>> class MyClass:
    ...     pass
    ...
    
    49, nhưng
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    41 không phải là
    >>> class MyClass:
    ...     pass
    ...
    
    51, vì vậy bạn không nên sử dụng kế thừa.
    Your derived classes should leverage most of their base class implementation. They must also model an is a relationship. A
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    41 class might also have an
    >>> class MyClass:
    ...     pass
    ...
    
    48 and a
    >>> class MyClass:
    ...     pass
    ...
    
    49, but a
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    41 is not an
    >>> class MyClass:
    ...     pass
    ...
    
    51, so you should not use inheritance.

  • Thực hiện một giao diện sẽ được sử dụng lại: Khi bạn muốn lớp của mình được sử dụng lại bởi một phần cụ thể trong ứng dụng của bạn, bạn sẽ thực hiện giao diện cần thiết trong lớp, nhưng bạn không cần cung cấp một lớp cơ sở hoặc kế thừa từ một lớp khác. When you want your class to be reused by a specific part of your application, you implement the required interface in your class, but you don’t need to provide a base class, or inherit from another class.

Bây giờ bạn có thể làm sạch ví dụ trên để chuyển sang chủ đề tiếp theo. Bạn có thể xóa tệp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
46 và sau đó sửa đổi mô -đun
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21 thành trạng thái ban đầu của nó:

>>> class MyClass:
...     pass
...
9

Bạn đã loại bỏ việc nhập mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
13 vì lớp
>>> class MyClass:
...     pass
...
51 không cần phải trừu tượng. Bạn cũng đã loại bỏ phương thức Tóm tắt
>>> class MyClass:
...     pass
...
97 khỏi nó vì nó không cung cấp bất kỳ triển khai nào.

Về cơ bản, bạn đang kế thừa việc thực hiện các thuộc tính

>>> class MyClass:
...     pass
...
48 và
>>> class MyClass:
...     pass
...
49 của lớp
>>> class MyClass:
...     pass
...
51 trong các lớp dẫn xuất của bạn. Vì
>>> class MyClass:
...     pass
...
47 chỉ là một giao diện cho phương thức
>>> class MyClass:
...     pass
...
93, bạn không cần phải thực hiện nó trong lớp cơ sở
>>> class MyClass:
...     pass
...
51.

Lưu ý cách lớp

>>> class MyClass:
...     pass
...
77 xuất phát từ
>>> class MyClass:
...     pass
...
60. Điều này có nghĩa là
>>> class MyClass:
...     pass
...
77 kế thừa việc triển khai và giao diện của
>>> class MyClass:
...     pass
...
60. Bạn có thể thấy phương pháp
>>> class MyClass:
...     pass
...
90 tận dụng việc triển khai lớp cơ sở như thế nào vì nó dựa vào kết quả từ
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
62 để thực hiện phiên bản của riêng mình.

Vấn đề vụ nổ lớp

Nếu bạn không cẩn thận, kế thừa có thể dẫn bạn đến một cấu trúc phân cấp khổng lồ của các lớp khó hiểu và duy trì. Điều này được gọi là vấn đề vụ nổ lớp.class explosion problem.

Bạn bắt đầu xây dựng một hệ thống phân cấp lớp gồm

>>> class MyClass:
...     pass
...
51 Các loại được sử dụng bởi
>>> class MyClass:
...     pass
...
45 để tính toán bảng lương. Bây giờ, bạn cần thêm một số chức năng cho các lớp đó, vì vậy chúng có thể được sử dụng với
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65 mới.

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65 theo dõi năng suất dựa trên vai trò của nhân viên. Có nhiều vai trò nhân viên khác nhau:

  • Người quản lý: Họ đi bộ xung quanh la hét với mọi người nói với họ phải làm gì. Họ là nhân viên được trả lương và kiếm được nhiều tiền hơn. They walk around yelling at people telling them what to do. They are salaried employees and make more money.
  • Thư ký: Họ làm tất cả các công việc giấy tờ cho các nhà quản lý và đảm bảo rằng mọi thứ được lập hóa đơn và thanh toán đúng hạn. Họ cũng là nhân viên được trả lương nhưng kiếm ít tiền hơn. They do all the paper work for managers and ensure that everything gets billed and payed on time. They are also salaried employees but make less money.
  • Nhân viên bán hàng: Họ thực hiện rất nhiều cuộc gọi điện thoại để bán sản phẩm. Họ có một mức lương, nhưng họ cũng nhận được hoa hồng để bán hàng. They make a lot of phone calls to sell products. They have a salary, but they also get commissions for sales.
  • Nhân viên nhà máy: Họ sản xuất các sản phẩm cho công ty. Họ được trả tiền theo giờ. They manufacture the products for the company. They are paid by the hour.

Với các yêu cầu đó, bạn bắt đầu thấy rằng

>>> class MyClass:
...     pass
...
51 và các lớp dẫn xuất của nó có thể thuộc về một nơi nào đó khác ngoài mô -đun
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21 vì bây giờ chúng cũng được sử dụng bởi
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65.

Bạn tạo một mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
70 và di chuyển các lớp ở đó:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
0

Việc triển khai vẫn giữ nguyên, nhưng bạn chuyển các lớp sang mô -đun

>>> class MyClass:
...     pass
...
94. Bây giờ, bạn thay đổi chương trình của mình để hỗ trợ thay đổi:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
1

Bạn chạy chương trình và xác minh rằng nó vẫn hoạt động:

>>> class MyClass:
...     pass
...
2

Với mọi thứ tại chỗ, bạn bắt đầu thêm các lớp mới:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
3

Đầu tiên, bạn thêm một lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
72 xuất phát từ
>>> class MyClass:
...     pass
...
60. Lớp hiển thị một phương pháp
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
74 sẽ được sử dụng bởi hệ thống năng suất. Phương pháp lấy
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
75 nhân viên đã làm việc.

Sau đó, bạn thêm

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76,
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
77 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
78 và sau đó thực hiện giao diện
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
74, do đó chúng có thể được sử dụng bởi hệ thống năng suất.

Bây giờ, bạn có thể thêm lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
80:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
4

Lớp học theo dõi nhân viên theo phương pháp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
81 lấy danh sách nhân viên và số giờ để theo dõi. Bây giờ bạn có thể thêm hệ thống năng suất vào chương trình của mình:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
5

Chương trình tạo ra một danh sách các nhân viên thuộc các loại khác nhau. Danh sách nhân viên được gửi đến hệ thống năng suất để theo dõi công việc của họ trong 40 giờ. Sau đó, cùng một danh sách nhân viên được gửi đến hệ thống bảng lương để tính toán bảng lương của họ.

Bạn có thể chạy chương trình để xem đầu ra:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
6

Chương trình cho thấy các nhân viên làm việc trong 40 giờ thông qua hệ thống năng suất. Sau đó, nó tính toán và hiển thị bảng lương cho mỗi nhân viên.

Chương trình hoạt động như mong đợi, nhưng bạn phải thêm bốn lớp mới để hỗ trợ các thay đổi. Khi các yêu cầu mới đến, hệ thống phân cấp lớp của bạn chắc chắn sẽ phát triển, dẫn đến vấn đề vụ nổ của lớp trong đó hệ thống phân cấp của bạn sẽ trở nên lớn đến mức chúng sẽ khó hiểu và duy trì.

Biểu đồ sau đây cho thấy hệ thống phân cấp lớp mới:

Sơ đồ cho thấy hệ thống phân cấp lớp đang phát triển như thế nào. Các yêu cầu bổ sung có thể có tác động theo cấp số nhân trong số lượng các lớp với thiết kế này.

Kế thừa nhiều lớp

Python là một trong số ít các ngôn ngữ lập trình hiện đại hỗ trợ nhiều kế thừa. Nhiều kế thừa là khả năng lấy một lớp từ nhiều lớp cơ sở cùng một lúc.

Nhiều kế thừa có tiếng xấu đến mức mà hầu hết các ngôn ngữ lập trình hiện đại không ủng hộ nó. Thay vào đó, các ngôn ngữ lập trình hiện đại hỗ trợ khái niệm giao diện. Trong các ngôn ngữ đó, bạn kế thừa từ một lớp cơ sở duy nhất và sau đó thực hiện nhiều giao diện, do đó lớp của bạn có thể được sử dụng lại trong các tình huống khác nhau.

Cách tiếp cận này đặt một số ràng buộc trong thiết kế của bạn. Bạn chỉ có thể kế thừa việc thực hiện một lớp bằng cách trực tiếp xuất phát từ nó. Bạn có thể triển khai nhiều giao diện, nhưng bạn có thể kế thừa việc triển khai nhiều lớp.

Hạn chế này tốt cho thiết kế phần mềm vì nó buộc bạn phải thiết kế các lớp học của mình với ít phụ thuộc hơn vào nhau. Bạn sẽ thấy sau trong bài viết này rằng bạn có thể tận dụng nhiều triển khai thông qua thành phần, điều này làm cho phần mềm linh hoạt hơn. Tuy nhiên, phần này là về nhiều kế thừa, vì vậy, hãy để xem cách thức hoạt động của nó.

Hóa ra đôi khi các thư ký tạm thời được thuê khi có quá nhiều giấy tờ để làm. Lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 thực hiện vai trò của
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76 trong bối cảnh
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65, nhưng đối với mục đích biên chế, đó là một
>>> class MyClass:
...     pass
...
70.

Bạn nhìn vào thiết kế lớp học của bạn. Nó đã phát triển một chút, nhưng bạn vẫn có thể hiểu nó hoạt động như thế nào. Có vẻ như bạn có hai tùy chọn:

  1. Bắt nguồn từ

    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    76: Bạn có thể lấy từ
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    76 để kế thừa phương thức
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    88 cho vai trò, sau đó ghi đè phương thức
    >>> class MyClass:
    ...     pass
    ...
    
    47 để thực hiện nó dưới dạng
    >>> class MyClass:
    ...     pass
    ...
    
    70.
    You can derive from
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    76 to inherit the
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    88 method for the role, and then override the
    >>> class MyClass:
    ...     pass
    ...
    
    47 method to implement it as an
    >>> class MyClass:
    ...     pass
    ...
    
    70.

  2. Bắt nguồn từ

    >>> class MyClass:
    ...     pass
    ...
    
    70: Bạn có thể lấy từ
    >>> class MyClass:
    ...     pass
    ...
    
    70 để kế thừa phương thức
    >>> class MyClass:
    ...     pass
    ...
    
    47, sau đó ghi đè phương thức
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    88 để thực hiện nó dưới dạng
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    76.
    You can derive from
    >>> class MyClass:
    ...     pass
    ...
    
    70 to inherit the
    >>> class MyClass:
    ...     pass
    ...
    
    47 method, and then override the
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    88 method to implement it as a
    >>> c = MyClass()
    >>> dir(c)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
    '__str__', '__subclasshook__', '__weakref__']
    
    76.

Sau đó, bạn nhớ rằng Python hỗ trợ nhiều kế thừa, vì vậy bạn quyết định xuất phát từ cả

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76 và
>>> class MyClass:
...     pass
...
70:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
7

Python cho phép bạn kế thừa từ hai lớp khác nhau bằng cách chỉ định chúng giữa dấu ngoặc đơn trong khai báo lớp.

Bây giờ, bạn sửa đổi chương trình của mình để thêm nhân viên thư ký tạm thời mới:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
8

Bạn chạy chương trình để kiểm tra nó:

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
9

Bạn nhận được một ngoại lệ

>>> class MyClass:
...     pass
...
37 nói rằng
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
99 Các đối số vị trí mà mong đợi, nhưng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
00 đã được đưa ra.

Điều này là do bạn đã lấy

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 đầu tiên từ
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76 và sau đó từ
>>> class MyClass:
...     pass
...
70, vì vậy trình thông dịch đang cố gắng sử dụng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
04 để khởi tạo đối tượng.

Được rồi, hãy để Lừa đảo ngược nó:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
0

Bây giờ, hãy chạy lại chương trình và xem điều gì sẽ xảy ra:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
1

Bây giờ có vẻ như bạn đang thiếu một tham số

>>> class MyClass:
...     pass
...
67, điều cần thiết để khởi tạo
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76, nhưng tham số đó không có ý nghĩa trong bối cảnh của
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 bởi vì nó là một
>>> class MyClass:
...     pass
...
70.

Có thể triển khai

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
09 sẽ giúp:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
2

Thử nó:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
3

Điều đó cũng không làm việc. Được rồi, đó là thời gian để bạn đi sâu vào thứ tự phân giải phương pháp Python (MRO) để xem những gì đang diễn ra.method resolution order (MRO) to see what’s going on.

Khi một phương thức hoặc thuộc tính của một lớp được truy cập, Python sử dụng lớp MRO để tìm nó. MRO cũng được

>>> class MyClass:
...     pass
...
64 sử dụng để xác định phương thức hoặc thuộc tính nào để gọi. Bạn có thể tìm hiểu thêm về
>>> class MyClass:
...     pass
...
64 trong Supercharge Các lớp của bạn với Python Super ().

Bạn có thể đánh giá lớp MRO

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 bằng trình thông dịch tương tác:

>>>

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
4

MRO hiển thị thứ tự mà Python sẽ tìm kiếm một thuộc tính hoặc phương thức phù hợp. Trong ví dụ, đây là những gì xảy ra khi chúng ta tạo đối tượng

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82:

  1. Phương pháp

    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    14 được gọi.

  2. Cuộc gọi

    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    15 khớp với
    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    16.

  3. >>> class MyClass:
    ...     pass
    ...
    
    70 gọi
    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    18, mà MRO sẽ khớp với
    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    04, được kế thừa từ
    >>> o = object()
    >>> dir(o)
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    
    20.

Bởi vì các tham số không phù hợp, một ngoại lệ

>>> class MyClass:
...     pass
...
37 được nâng lên.

Bạn có thể bỏ qua MRO bằng cách đảo ngược thứ tự kế thừa và gọi trực tiếp

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
22 như sau:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
5

Điều đó giải quyết vấn đề tạo đối tượng, nhưng bạn sẽ gặp phải một vấn đề tương tự khi cố gắng tính toán bảng lương. Bạn có thể chạy chương trình để xem vấn đề:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
6

Vấn đề bây giờ là bởi vì bạn đã đảo ngược thứ tự thừa kế, MRO đang tìm phương pháp

>>> class MyClass:
...     pass
...
47 của
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
24 trước phương pháp trong
>>> class MyClass:
...     pass
...
70. Bạn cần ghi đè
>>> class MyClass:
...     pass
...
47 trong
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 và gọi đúng cách thực hiện từ nó:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
7

Phương pháp

>>> class MyClass:
...     pass
...
97 trực tiếp gọi
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
29 để đảm bảo rằng bạn nhận được kết quả chính xác. Bạn có thể chạy lại chương trình để xem nó hoạt động:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
8

Chương trình hiện hoạt động như mong đợi bởi vì bạn buộc phải thứ tự giải quyết phương thức bằng cách nói rõ ràng với trình thông dịch mà chúng tôi muốn sử dụng phương pháp nào.

Như bạn có thể thấy, nhiều kế thừa có thể gây nhầm lẫn, đặc biệt là khi bạn gặp phải vấn đề kim cương.

Biểu đồ sau đây cho thấy vấn đề kim cương trong hệ thống phân cấp lớp của bạn:

Sơ đồ cho thấy vấn đề kim cương với thiết kế lớp hiện tại.

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 sử dụng nhiều kế thừa để xuất phát từ hai lớp cuối cùng cũng xuất phát từ
>>> class MyClass:
...     pass
...
51. Điều này làm cho hai đường dẫn đến lớp cơ sở
>>> class MyClass:
...     pass
...
51, đó là điều bạn muốn tránh trong các thiết kế của mình.

Vấn đề kim cương xuất hiện khi bạn sử dụng nhiều kế thừa và xuất phát từ hai lớp có lớp cơ sở chung. Điều này có thể khiến phiên bản sai của phương thức được gọi.

Như bạn đã thấy, Python cung cấp một cách để buộc đúng phương pháp được gọi và phân tích MRO có thể giúp bạn hiểu vấn đề.

Tuy nhiên, khi bạn gặp phải vấn đề kim cương, nó tốt hơn là suy nghĩ lại thiết kế. Bây giờ bạn sẽ thực hiện một số thay đổi để tận dụng nhiều kế thừa, tránh vấn đề kim cương.

Các lớp dẫn xuất

>>> class MyClass:
...     pass
...
51 được sử dụng bởi hai hệ thống khác nhau:

  1. Hệ thống năng suất theo dõi năng suất của nhân viên. that tracks employee productivity.

  2. Hệ thống biên chế tính toán bảng lương của nhân viên. that calculates the employee payroll.

Điều này có nghĩa là tất cả mọi thứ liên quan đến năng suất nên được cùng nhau trong một mô -đun và mọi thứ liên quan đến bảng lương nên được cùng nhau ở cùng. Bạn có thể bắt đầu thay đổi mô -đun năng suất:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
9

Mô -đun

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
34 thực hiện lớp
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65, cũng như các vai trò liên quan mà nó hỗ trợ. Các lớp thực hiện giao diện
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
74 theo yêu cầu của hệ thống, nhưng chúng không có nguồn gốc từ
>>> class MyClass:
...     pass
...
51.

Bạn có thể làm tương tự với mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
0

Mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21 thực hiện
>>> class MyClass:
...     pass
...
45, tính toán bảng lương cho nhân viên. Nó cũng thực hiện các lớp chính sách cho bảng lương. Như bạn có thể thấy, các lớp chính sách don don bắt nguồn từ
>>> class MyClass:
...     pass
...
51 nữa.

Bây giờ bạn có thể thêm các lớp cần thiết vào mô -đun

>>> class MyClass:
...     pass
...
94:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
1

Mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
70 Nhập các chính sách và vai trò từ các mô -đun khác và thực hiện các loại
>>> class MyClass:
...     pass
...
51 khác nhau. Bạn vẫn đang sử dụng nhiều kế thừa để kế thừa việc thực hiện các lớp chính sách lương và vai trò năng suất, nhưng việc thực hiện từng lớp chỉ cần đối phó với việc khởi tạo.

Lưu ý rằng bạn vẫn cần khởi tạo rõ ràng các chính sách lương trong các nhà xây dựng. Bạn có thể thấy rằng các khởi tạo của

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
72 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76 là giống hệt nhau. Ngoài ra, các khởi đầu của
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
78 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 là như nhau.

Bạn sẽ không muốn có loại sao chép mã này trong các thiết kế phức tạp hơn, vì vậy bạn phải cẩn thận khi thiết kế phân cấp lớp.

Ở đây, sơ đồ UML cho thiết kế mới:

Sơ đồ cho thấy các mối quan hệ để xác định

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
76 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
82 bằng cách sử dụng nhiều kế thừa, nhưng tránh được vấn đề kim cương.

Bạn có thể chạy chương trình và xem cách thức hoạt động:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
2

Bạn đã thấy cách thừa kế và nhiều kế thừa hoạt động trong Python. Bây giờ bạn có thể khám phá chủ đề của sáng tác.

Thành phần trong Python

Thành phần là một khái niệm thiết kế định hướng đối tượng mô hình A có mối quan hệ. Trong thành phần, một lớp được gọi là tổng hợp chứa một đối tượng của một lớp khác được gọi là thành phần. Nói cách khác, một lớp tổng hợp có một thành phần của một lớp khác. is an object oriented design concept that models a has a relationship. In composition, a class known as composite contains an object of another class known to as component. In other words, a composite class has a component of another class.

Thành phần cho phép các lớp tổng hợp sử dụng lại việc thực hiện các thành phần mà nó chứa. Lớp tổng hợp không kế thừa giao diện lớp thành phần, nhưng nó có thể tận dụng việc triển khai của nó.

Mối quan hệ thành phần giữa hai lớp được coi là kết hợp lỏng lẻo. Điều đó có nghĩa là các thay đổi đối với lớp thành phần hiếm khi ảnh hưởng đến lớp tổng hợp và thay đổi thành lớp tổng hợp không bao giờ ảnh hưởng đến lớp thành phần.

Điều này cung cấp khả năng thích ứng tốt hơn để thay đổi và cho phép các ứng dụng giới thiệu các yêu cầu mới mà không ảnh hưởng đến mã hiện có.

Khi xem xét hai thiết kế phần mềm cạnh tranh, một dựa trên kế thừa và một dựa trên thành phần, giải pháp thành phần thường là linh hoạt nhất. Bây giờ bạn có thể nhìn vào cách thức thành phần hoạt động.

Bạn đã sử dụng thành phần trong các ví dụ của chúng tôi. Nếu bạn nhìn vào lớp

>>> class MyClass:
...     pass
...
51, bạn sẽ thấy rằng nó chứa hai thuộc tính:

  1. >>> class MyClass:
    ...     pass
    ...
    
    48 để xác định một nhân viên.
    to identify an employee.
  2. >>> class MyClass:
    ...     pass
    ...
    
    49 để chứa tên của nhân viên.
    to contain the name of the employee.

Hai thuộc tính này là các đối tượng mà lớp

>>> class MyClass:
...     pass
...
51 có. Do đó, bạn có thể nói rằng
>>> class MyClass:
...     pass
...
51 có
>>> class MyClass:
...     pass
...
48 và có tên.has an
>>> class MyClass:
...     pass
...
48 and has a name.

Một thuộc tính khác cho

>>> class MyClass:
...     pass
...
51 có thể là
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
3

Bạn đã triển khai một lớp địa chỉ cơ bản chứa các thành phần thông thường cho một địa chỉ. Bạn đã thực hiện thuộc tính

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
59 tùy chọn vì không phải tất cả các địa chỉ sẽ có thành phần đó.

Bạn đã triển khai

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
60 để cung cấp một đại diện khá lớn của
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58. Bạn có thể thấy việc triển khai này trong trình thông dịch tương tác:

>>>

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
4

Khi bạn

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
62 biến
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
63, phương pháp đặc biệt
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
60 được gọi. Vì bạn đã quá tải phương thức để trả về một chuỗi được định dạng dưới dạng địa chỉ, bạn sẽ nhận được một đại diện tốt, dễ đọc. Toán tử và quá tải chức năng trong các lớp Python tùy chỉnh cung cấp một cái nhìn tổng quan tốt về các phương thức đặc biệt có sẵn trong các lớp có thể được thực hiện để tùy chỉnh hành vi của các đối tượng của bạn.

Bây giờ bạn có thể thêm

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 vào lớp
>>> class MyClass:
...     pass
...
51 thông qua bố cục:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
5

Bạn khởi tạo thuộc tính

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
63 cho
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
68 ngay bây giờ để làm cho nó tùy chọn, nhưng bằng cách đó, bây giờ bạn có thể gán một
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 cho
>>> class MyClass:
...     pass
...
51. Cũng lưu ý rằng không có tài liệu tham khảo trong mô -đun
>>> class MyClass:
...     pass
...
94 đến mô -đun
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
72.

Thành phần là một mối quan hệ được kết hợp lỏng lẻo mà thường không yêu cầu lớp tổng hợp phải có kiến ​​thức về thành phần.

Sơ đồ UML đại diện cho mối quan hệ giữa

>>> class MyClass:
...     pass
...
51 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 trông như thế này:

Sơ đồ cho thấy mối quan hệ thành phần cơ bản giữa

>>> class MyClass:
...     pass
...
51 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58.

Bây giờ bạn có thể sửa đổi lớp

>>> class MyClass:
...     pass
...
45 để tận dụng thuộc tính
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
63 trong
>>> class MyClass:
...     pass
...
51:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
6

Bạn kiểm tra xem đối tượng

>>> class MyClass:
...     pass
...
94 có địa chỉ không và nếu có, bạn in nó. Bây giờ bạn có thể sửa đổi chương trình để gán một số địa chỉ cho nhân viên:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
7

Bạn đã thêm một vài địa chỉ vào các đối tượng

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
81 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
82. Khi bạn chạy chương trình, bạn sẽ thấy các địa chỉ được in:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
8

Lưu ý cách đầu ra bảng lương cho các đối tượng

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
81 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
82 hiển thị các địa chỉ nơi kiểm tra được gửi.

Lớp

>>> class MyClass:
...     pass
...
51 tận dụng việc triển khai lớp
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 mà không có bất kỳ kiến ​​thức nào về đối tượng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 là gì hoặc cách nó thể hiện. Kiểu thiết kế này linh hoạt đến mức bạn có thể thay đổi lớp
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 mà không có bất kỳ tác động nào đến lớp
>>> class MyClass:
...     pass
...
51.

Thiết kế linh hoạt với bố cục

Thành phần linh hoạt hơn sự kế thừa vì nó mô hình hóa một mối quan hệ được ghép nối lỏng lẻo. Các thay đổi đối với một lớp thành phần có hiệu ứng tối thiểu hoặc không có tác dụng đối với lớp tổng hợp. Thiết kế dựa trên bố cục phù hợp hơn để thay đổi.

Bạn thay đổi hành vi bằng cách cung cấp các thành phần mới thực hiện các hành vi đó thay vì thêm các lớp mới vào hệ thống phân cấp của bạn.

Hãy xem ví dụ về nhiều kế thừa ở trên. Hãy tưởng tượng làm thế nào các chính sách biên chế mới sẽ ảnh hưởng đến thiết kế. Cố gắng hình dung phân cấp lớp sẽ trông như thế nào nếu cần vai trò mới. Như bạn đã thấy trước đây, việc phụ thuộc quá nhiều vào thừa kế có thể dẫn đến vụ nổ giai cấp.

Vấn đề lớn nhất không phải là số lượng các lớp trong thiết kế của bạn, nhưng kết hợp chặt chẽ các mối quan hệ giữa các lớp đó. Các lớp kết hợp chặt chẽ ảnh hưởng đến nhau khi thay đổi được giới thiệu.

Trong phần này, bạn sẽ sử dụng thành phần để thực hiện một thiết kế tốt hơn vẫn phù hợp với các yêu cầu của

>>> class MyClass:
...     pass
...
45 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65.

Bạn có thể bắt đầu bằng cách thực hiện chức năng của

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65:

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
9

Lớp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65 xác định một số vai trò bằng cách sử dụng một định danh chuỗi được ánh xạ tới một lớp vai trò thực hiện vai trò. Nó phơi bày một phương thức
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
94, với một định danh vai trò, trả về đối tượng loại vai trò. Nếu vai trò không được tìm thấy, thì ngoại lệ
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
95 được nâng lên.

Nó cũng phơi bày các chức năng trước đó trong phương pháp

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
96, trong đó đưa ra danh sách nhân viên, nó theo dõi năng suất của những nhân viên đó.

Bây giờ bạn có thể thực hiện các lớp vai trò khác nhau:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
0

Mỗi vai trò bạn đã thực hiện để lộ một

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
97 có số lượng
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
75 hoạt động. Các phương pháp trả về một chuỗi đại diện cho các nhiệm vụ.

Các lớp vai trò độc lập với nhau, nhưng chúng phơi bày cùng một giao diện, vì vậy chúng có thể hoán đổi cho nhau. Bạn sẽ thấy sau đó chúng được sử dụng trong ứng dụng như thế nào.

Bây giờ, bạn có thể triển khai

>>> class MyClass:
...     pass
...
45 cho ứng dụng:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
1

>>> class MyClass:
...     pass
...
45 giữ một cơ sở dữ liệu nội bộ về chính sách biên chế cho mỗi nhân viên. Nó phơi bày một
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
01 rằng, với một nhân viên
>>> class MyClass:
...     pass
...
48, trả lại chính sách biên chế của mình. Nếu một
>>> class MyClass:
...     pass
...
48 được chỉ định không tồn tại trong hệ thống, thì phương thức này sẽ tăng ngoại lệ
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
95.

Việc thực hiện

>>> class MyClass:
...     pass
...
47 hoạt động giống như trước đây. Nó lấy một danh sách các nhân viên, tính toán bảng lương và in kết quả.

Bây giờ bạn có thể thực hiện các lớp chính sách bảng lương:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
2

Trước tiên, bạn thực hiện một lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06 phục vụ như một lớp cơ sở cho tất cả các chính sách biên chế. Lớp này theo dõi
>>> class MyClass:
...     pass
...
74, phổ biến cho tất cả các chính sách bảng lương.

Các lớp chính sách khác xuất phát từ

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06. Chúng tôi sử dụng kế thừa ở đây vì chúng tôi muốn tận dụng việc thực hiện
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06. Ngoài ra,
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
10,
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
11 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
12 là
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06.are a
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06.

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
10 được khởi tạo với giá trị
>>> class MyClass:
...     pass
...
67 sau đó được sử dụng trong
>>> class MyClass:
...     pass
...
47.
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
11 được khởi tạo với
>>> class MyClass:
...     pass
...
75 và thực hiện
>>> class MyClass:
...     pass
...
47 bằng cách tận dụng lớp cơ sở
>>> class MyClass:
...     pass
...
74.

Lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
12 xuất phát từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
10 vì họ muốn kế thừa việc thực hiện. Nó được khởi tạo với các tham số
>>> class MyClass:
...     pass
...
67, nhưng nó cũng yêu cầu tham số
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
24.

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
24 được sử dụng để tính toán
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
26, được thực hiện như một thuộc tính để nó được tính toán khi được yêu cầu. Trong ví dụ, chúng tôi giả định rằng việc bán hàng xảy ra cứ sau 5 giờ hoạt động và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
26 là số lần bán hàng giá trị
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
24.

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
12 thực hiện phương pháp
>>> class MyClass:
...     pass
...
47 bằng cách đầu tiên tận dụng việc thực hiện trong
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
10 và sau đó thêm hoa hồng được tính toán.

Bây giờ bạn có thể thêm một lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
32 để quản lý địa chỉ nhân viên:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
3

Lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
32 giữ cơ sở dữ liệu nội bộ của các đối tượng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 cho mỗi nhân viên. Nó phơi bày một phương thức
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
35 trả về địa chỉ của nhân viên được chỉ định
>>> class MyClass:
...     pass
...
48. Nếu nhân viên
>>> class MyClass:
...     pass
...
48 không tồn tại, thì nó sẽ tăng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
95.

Việc triển khai lớp

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 vẫn giống như trước đây:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
4

Lớp quản lý các thành phần địa chỉ và cung cấp một đại diện khá lớn của một địa chỉ.

Cho đến nay, các lớp mới đã được mở rộng để hỗ trợ nhiều chức năng hơn, nhưng không có thay đổi đáng kể nào đối với thiết kế trước đó. Điều này sẽ thay đổi với thiết kế của mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
70 và các lớp của nó.

Bạn có thể bắt đầu bằng cách triển khai một lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
41:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
5

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
41 theo dõi tất cả các nhân viên trong công ty. Đối với mỗi nhân viên, nó theo dõi
>>> class MyClass:
...     pass
...
48,
>>> class MyClass:
...     pass
...
49 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
45. Nó có một ví dụ của
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65,
>>> class MyClass:
...     pass
...
45 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
32. Những trường hợp này được sử dụng để tạo nhân viên.has an instance of the
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
65, the
>>> class MyClass:
...     pass
...
45, and the
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
32. These instances are used to create employees.

Nó phơi bày một tài sản

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
49 trả về danh sách nhân viên. Các đối tượng
>>> class MyClass:
...     pass
...
51 được tạo theo phương pháp nội bộ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
51. Lưu ý rằng bạn không có các loại lớp
>>> class MyClass:
...     pass
...
51 khác nhau. Bạn chỉ cần thực hiện một lớp
>>> class MyClass:
...     pass
...
51 duy nhất:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
6

Lớp

>>> class MyClass:
...     pass
...
51 được khởi tạo với các thuộc tính
>>> class MyClass:
...     pass
...
48,
>>> class MyClass:
...     pass
...
49 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
63. Nó cũng yêu cầu năng suất
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
45 cho nhân viên và chính sách
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
59.

Lớp học cho thấy một phương pháp

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
88 mất nhiều giờ làm việc. Phương pháp này lần đầu tiên lấy
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
61 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
45. Nói cách khác, nó ủy thác cho đối tượng
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
45 để thực hiện nhiệm vụ của mình.

Theo cách tương tự, nó giao cho đối tượng

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
59 để theo dõi công việc
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
75.
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
59, như bạn đã thấy, sử dụng những giờ đó để tính toán bảng lương nếu cần.

Biểu đồ sau đây cho thấy thiết kế thành phần được sử dụng:

Sơ đồ cho thấy thiết kế của các chính sách dựa trên thành phần. Có một

>>> class MyClass:
...     pass
...
51 bao gồm các đối tượng dữ liệu khác như
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 và phụ thuộc vào giao diện
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
69 và
>>> class MyClass:
...     pass
...
91 để ủy thác công việc. Có nhiều triển khai của các giao diện này.

Bây giờ bạn có thể sử dụng thiết kế này trong chương trình của mình:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
7

Bạn có thể chạy chương trình để xem đầu ra của nó:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
8

Thiết kế này được gọi là thiết kế dựa trên chính sách, nơi các lớp bao gồm các chính sách và họ ủy thác cho các chính sách đó để thực hiện công việc.

Thiết kế dựa trên chính sách đã được giới thiệu trong cuốn sách Thiết kế C ++ hiện đại và nó sử dụng metaproming mẫu trong C ++ để đạt được kết quả.

Python không hỗ trợ các mẫu, nhưng bạn có thể đạt được kết quả tương tự bằng cách sử dụng thành phần, như bạn đã thấy trong ví dụ trên.

Loại thiết kế này cung cấp cho bạn tất cả sự linh hoạt mà bạn sẽ cần khi yêu cầu thay đổi. Hãy tưởng tượng bạn cần thay đổi cách tính toán bảng lương cho một đối tượng trong thời gian chạy.

Tùy chỉnh hành vi với bố cục

Nếu thiết kế của bạn dựa vào kế thừa, bạn cần tìm cách thay đổi loại đối tượng để thay đổi hành vi của nó. Với thành phần, bạn chỉ cần thay đổi chính sách mà đối tượng sử dụng.

Hãy tưởng tượng rằng

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
81 của chúng tôi đột nhiên trở thành một nhân viên tạm thời được trả tiền theo giờ. Bạn có thể sửa đổi đối tượng trong quá trình thực hiện chương trình theo cách sau:

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
9

Chương trình có được danh sách nhân viên từ

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
41 và lấy nhân viên đầu tiên, đó là người quản lý mà chúng tôi muốn. Sau đó, nó tạo ra một
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
11 mới được khởi tạo ở mức 55 đô la mỗi giờ và gán nó cho đối tượng Trình quản lý.

Chính sách mới hiện được sử dụng bởi

>>> class MyClass:
...     pass
...
45 sửa đổi hành vi hiện có. Bạn có thể chạy lại chương trình để xem kết quả:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
0

Kiểm tra Mary Poppins, người quản lý của chúng tôi, hiện có giá 2200 đô la thay vì mức lương cố định là 3000 đô la mà cô ấy có mỗi tuần.

Lưu ý cách chúng tôi đã thêm quy tắc kinh doanh vào chương trình mà không thay đổi bất kỳ lớp nào hiện có. Xem xét loại thay đổi nào sẽ được yêu cầu với một thiết kế thừa kế.

Bạn sẽ phải tạo ra một lớp mới và thay đổi loại nhân viên của người quản lý. Không có cơ hội bạn có thể thay đổi chính sách vào thời điểm chạy.

Lựa chọn giữa thừa kế và thành phần trong Python

Cho đến nay, bạn đã thấy cách thừa kế và sáng tác hoạt động trong Python. Bạn đã thấy rằng các lớp có nguồn gốc kế thừa giao diện và triển khai các lớp cơ sở của họ. Bạn cũng đã thấy rằng thành phần cho phép bạn sử dụng lại việc thực hiện một lớp khác.

Bạn đã thực hiện hai giải pháp cho cùng một vấn đề. Giải pháp đầu tiên sử dụng nhiều kế thừa và thành phần thứ hai được sử dụng.

Bạn cũng đã thấy rằng gõ vịt Python cho phép bạn sử dụng lại các đối tượng với các phần hiện có của chương trình bằng cách triển khai giao diện mong muốn. Trong Python, không cần thiết phải xuất phát từ một lớp cơ sở để các lớp học của bạn được tái sử dụng.

Tại thời điểm này, bạn có thể hỏi khi nào nên sử dụng kế thừa so với thành phần trong Python. Cả hai đều cho phép tái sử dụng mã. Kế thừa và thành phần có thể giải quyết các vấn đề tương tự trong các chương trình Python của bạn.

Lời khuyên chung là sử dụng mối quan hệ tạo ra ít phụ thuộc hơn giữa hai lớp. Mối quan hệ này là thành phần. Tuy nhiên, sẽ có những lúc thừa kế sẽ có ý nghĩa hơn.

Các phần sau đây cung cấp một số hướng dẫn để giúp bạn đưa ra lựa chọn đúng đắn giữa kế thừa và thành phần trong Python.

Kế thừa cho mô hình “là một mối quan hệ”

Kế thừa chỉ nên được sử dụng để mô hình hóa A là một mối quan hệ. Nguyên tắc thay thế Liskov, nói rằng một đối tượng loại

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7, kế thừa từ
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8, có thể thay thế một đối tượng loại
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8 mà không làm thay đổi các thuộc tính mong muốn của chương trình.is a relationship. Liskov’s substitution principle says that an object of type
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
7, which inherits from
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8, can replace an object of type
# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
8 without altering the desirable properties of a program.

Nguyên tắc thay thế Liskov, là hướng dẫn quan trọng nhất để xác định xem việc kế thừa có phải là giải pháp thiết kế phù hợp hay không. Tuy nhiên, câu trả lời có thể không đơn giản trong mọi tình huống. May mắn thay, có một thử nghiệm đơn giản mà bạn có thể sử dụng để xác định xem thiết kế của bạn có tuân theo nguyên tắc thay thế của Liskov hay không.

Hãy nói rằng bạn có một lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
78 cung cấp một triển khai và giao diện mà bạn muốn sử dụng lại trong một lớp khác
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
79. Suy nghĩ ban đầu của bạn là bạn có thể lấy
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
79 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
78 và kế thừa cả giao diện và triển khai. Để chắc chắn rằng đây là thiết kế phù hợp, bạn làm theo các bước luận văn:

  1. Đánh giá

    >>> class MyError:
    ...     pass
    ...
    >>> raise MyError()
    
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    79 là một
    >>> class MyError:
    ...     pass
    ...
    >>> raise MyError()
    
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    78: Hãy suy nghĩ về mối quan hệ này và biện minh cho nó. Nó có ý nghĩa không?
    Think about this relationship and justify it. Does it make sense?

  2. Đánh giá

    >>> class MyError:
    ...     pass
    ...
    >>> raise MyError()
    
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    78 là một
    >>> class MyError:
    ...     pass
    ...
    >>> raise MyError()
    
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    79: đảo ngược mối quan hệ và biện minh cho nó. Nó cũng có ý nghĩa?
    Reverse the relationship and justify it. Does it also make sense?

Nếu bạn có thể biện minh cho cả hai mối quan hệ, thì bạn không bao giờ nên thừa hưởng các lớp đó từ nhau. Hãy cùng nhìn vào một ví dụ cụ thể hơn.

Bạn có một lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 phơi bày một thuộc tính
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
87. Bạn cần một lớp
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88, cũng có
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
87. Có vẻ như
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 là một loại
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 đặc biệt, vì vậy có lẽ bạn có thể xuất phát từ nó và tận dụng cả giao diện và triển khai.

Trước khi bạn nhảy vào việc thực hiện, bạn sử dụng nguyên tắc thay thế Liskov, để đánh giá mối quan hệ.

A

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 là
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 vì diện tích của nó được tính toán từ sản phẩm của
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 lần
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95 của nó. Hạn chế là
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
96 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
97 phải bằng nhau.is a
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 because its area is calculated from the product of its
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 times its
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95. The constraint is that
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
96 and
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
97 must be equal.

Nó có ý nghĩa. Bạn có thể biện minh cho mối quan hệ và giải thích tại sao

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 là
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86. Hãy để đảo ngược mối quan hệ để xem nó có ý nghĩa không.is a
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86. Let’s reverse the relationship to see if it makes sense.

A

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 là
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 vì diện tích của nó được tính toán từ sản phẩm của
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 lần
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95 của nó. Sự khác biệt là
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
04 và
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
05 có thể thay đổi độc lập.is a
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 because its area is calculated from the product of its
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 times its
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95. The difference is that
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
04 and
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
05 can change independently.

Nó cũng có ý nghĩa. Bạn có thể biện minh cho mối quan hệ và mô tả các ràng buộc đặc biệt cho mỗi lớp. Đây là một dấu hiệu tốt cho thấy hai lớp này không bao giờ nên xuất phát từ nhau.

Bạn có thể đã thấy các ví dụ khác xuất phát từ

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 để giải thích kế thừa. Bạn có thể hoài nghi với bài kiểm tra nhỏ mà bạn vừa làm. Đủ công bằng. Hãy để viết một chương trình minh họa vấn đề với việc lấy
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86.

Đầu tiên, bạn thực hiện

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86. Bạn thậm chí sẽ gói gọn các thuộc tính để đảm bảo rằng tất cả các ràng buộc được đáp ứng:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
1

Lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 được khởi tạo với
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94, và nó cung cấp một thuộc tính
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
87 trả về khu vực.
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 được gói gọn để tránh thay đổi chúng trực tiếp.

Bây giờ, bạn rút ra

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 và ghi đè giao diện cần thiết để đáp ứng các ràng buộc của
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
2

Lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 được khởi tạo bằng
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
21, được sử dụng để khởi tạo cả hai thành phần của lớp cơ sở. Bây giờ, bạn viết một chương trình nhỏ để kiểm tra hành vi:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
3

Chương trình tạo ra

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 và khẳng định rằng
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
87 của họ được tính toán chính xác. Bạn có thể chạy chương trình và thấy rằng mọi thứ đều là
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
25 cho đến nay:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
4

Chương trình thực hiện chính xác, do đó, có vẻ như

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 chỉ là một trường hợp đặc biệt của
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86.

Sau này, bạn cần hỗ trợ thay đổi kích thước các đối tượng

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86, vì vậy bạn thực hiện các thay đổi phù hợp với lớp:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
5

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
29 mất
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
30 và
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
31 cho đối tượng. Bạn có thể thêm mã sau vào chương trình để xác minh rằng nó hoạt động chính xác:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
6

Bạn thay đổi kích thước đối tượng hình chữ nhật và khẳng định rằng khu vực mới là chính xác. Bạn có thể chạy chương trình để xác minh hành vi:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
4

Khẳng định vượt qua, và bạn thấy rằng chương trình chạy chính xác.

Vì vậy, điều gì xảy ra nếu bạn thay đổi kích thước một hình vuông? Sửa đổi chương trình và cố gắng sửa đổi đối tượng

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
32:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
8

Bạn chuyển các tham số tương tự đến

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
33 mà bạn đã sử dụng với
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
34 và in khu vực. Khi bạn chạy chương trình bạn thấy:

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
9

Chương trình cho thấy khu vực mới là

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
35 như đối tượng
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
34. Vấn đề bây giờ là đối tượng
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
32 không còn đáp ứng ràng buộc lớp
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 mà
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94 phải bằng nhau.

Làm thế nào bạn có thể khắc phục vấn đề đó? Bạn có thể thử một số cách tiếp cận, nhưng tất cả chúng sẽ rất khó xử. Bạn có thể ghi đè

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
29 trong
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
32 và bỏ qua tham số
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
94, nhưng điều đó sẽ gây nhầm lẫn cho những người nhìn vào các phần khác của chương trình trong đó
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
44 đang được thay đổi kích thước và một số trong số chúng không nhận được các khu vực dự kiến ​​vì chúng thực sự là
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
45.

Trong một chương trình nhỏ như thế này, có thể dễ dàng phát hiện ra nguyên nhân của hành vi kỳ lạ, nhưng trong một chương trình phức tạp hơn, vấn đề sẽ khó tìm hơn.

Thực tế là nếu bạn có thể biện minh cho mối quan hệ thừa kế giữa hai lớp cả hai cách, bạn không nên lấy một lớp này từ một lớp khác.

Trong ví dụ, nó không có ý nghĩa rằng

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 kế thừa giao diện và triển khai
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
29 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86. Điều đó không có nghĩa là các đối tượng
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 có thể được thay đổi kích thước. Nó có nghĩa là giao diện là khác nhau vì nó chỉ cần tham số
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
21.

Sự khác biệt trong giao diện này biện minh cho việc không lấy được

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
88 từ
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
86 như bài kiểm tra ở trên nên được khuyên.

Trộn các tính năng với các lớp mixin

Một trong những cách sử dụng của nhiều kế thừa trong Python là mở rộng một tính năng của lớp thông qua mixin. Mixin là một lớp cung cấp các phương pháp cho các lớp khác nhưng không được coi là một lớp cơ sở.mixin is a class that provides methods to other classes but are not considered a base class.

Một mixin cho phép các lớp khác sử dụng lại giao diện và triển khai của nó mà không trở thành một lớp siêu. Họ thực hiện một hành vi độc đáo có thể được tổng hợp cho các lớp không liên quan khác. Chúng tương tự như sáng tác nhưng chúng tạo ra một mối quan hệ mạnh mẽ hơn.

Hãy nói rằng bạn muốn chuyển đổi các đối tượng thuộc một số loại nhất định trong ứng dụng của bạn thành biểu diễn từ điển của đối tượng. Bạn có thể cung cấp một phương thức

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53 trong mỗi lớp mà bạn muốn hỗ trợ tính năng này, nhưng việc triển khai
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53 dường như rất giống nhau.

Đây có thể là một ứng cử viên tốt cho một mixin. Bạn bắt đầu bằng cách sửa đổi một chút lớp

>>> class MyClass:
...     pass
...
51 từ ví dụ thành phần:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
0

Sự thay đổi là rất nhỏ. Bạn vừa thay đổi các thuộc tính

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
45 và
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
59 là nội bộ bằng cách thêm một dấu gạch dưới hàng đầu vào tên của chúng. Bạn sẽ thấy sớm tại sao bạn đang thực hiện thay đổi đó.

Bây giờ, bạn thêm lớp

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
58:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
1

Lớp

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
58 phơi bày phương pháp
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53 trả về biểu diễn của chính nó như một từ điển. Phương pháp này được thực hiện dưới dạng hiểu biết
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
61 cho biết, Tạo bản đồ từ điển
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
62 đến
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
63 cho mỗi mục trong
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
64 nếu
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
62 không phải là nội bộ.

Như bạn đã thấy lúc đầu, tạo một lớp kế thừa một số thành viên từ

>>> class MyClass:
...     pass
...
32 và một trong những thành viên đó là
>>> class MyClass:
...     pass
...
30, về cơ bản là ánh xạ của tất cả các thuộc tính trong một đối tượng theo giá trị của chúng.

Bạn lặp lại tất cả các mục trong

>>> class MyClass:
...     pass
...
30 và lọc ra các mục có tên bắt đầu bằng dấu gạch dưới bằng
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
69.

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
70 Kiểm tra giá trị được chỉ định. Nếu giá trị là một
>>> class MyClass:
...     pass
...
32, thì có vẻ như xem nó cũng có thành viên
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53 và sử dụng nó để thể hiện đối tượng. Nếu không, nó trả về một đại diện chuỗi. Nếu giá trị không phải là
>>> class MyClass:
...     pass
...
32, thì nó chỉ cần trả về giá trị.is an
>>> class MyClass:
...     pass
...
32, then it looks to see if it also has a
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53 member and uses it to represent the object. Otherwise, it returns a string representation. If the value is not an
>>> class MyClass:
...     pass
...
32, then it simply returns the value.

Bạn có thể sửa đổi lớp

>>> class MyClass:
...     pass
...
51 để hỗ trợ mixin này:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
2

Tất cả bạn phải làm là thừa hưởng

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
58 để hỗ trợ chức năng. Sẽ rất tuyệt khi hỗ trợ chức năng tương tự trong lớp
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58, do đó, thuộc tính
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
77 được thể hiện theo cùng một cách:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
3

Bạn áp dụng mixin cho lớp

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 để hỗ trợ tính năng. Bây giờ, bạn có thể viết một chương trình nhỏ để kiểm tra nó:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
4

Chương trình thực hiện một

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
79 chuyển đổi từ điển thành chuỗi JSON bằng cách sử dụng thụt vào để đầu ra trông tốt hơn.

Sau đó, nó lặp lại thông qua tất cả các nhân viên, in biểu diễn từ điển được cung cấp bởi

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
53. Bạn có thể chạy chương trình để xem đầu ra của nó:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
5

Bạn đã tận dụng việc triển khai

>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
58 trong cả hai lớp
>>> class MyClass:
...     pass
...
51 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 ngay cả khi chúng không liên quan. Bởi vì
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
58 chỉ cung cấp hành vi, rất dễ sử dụng lại với các lớp khác mà không gây ra vấn đề.

Sáng tác để mô hình "có một mối quan hệ"

Mô hình sáng tác A có một mối quan hệ. Với thành phần, lớp

>>> class MyClass:
...     pass
...
03 có một thể hiện của lớp
>>> class MyClass:
...     pass
...
04 và có thể tận dụng việc thực hiện nó. Lớp
>>> class MyClass:
...     pass
...
04 có thể được sử dụng lại trong các lớp khác hoàn toàn không liên quan đến
>>> class MyClass:
...     pass
...
03.has a relationship. With composition, a class
>>> class MyClass:
...     pass
...
03 has an instance of class
>>> class MyClass:
...     pass
...
04 and can leverage its implementation. The
>>> class MyClass:
...     pass
...
04 class can be reused in other classes completely unrelated to the
>>> class MyClass:
...     pass
...
03.

Trong ví dụ thành phần ở trên, lớp

>>> class MyClass:
...     pass
...
51 có đối tượng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58.
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 thực hiện tất cả các chức năng để xử lý các địa chỉ và nó có thể được sử dụng lại bởi các lớp khác.has an
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 object.
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 implements all the functionality to handle addresses, and it can be reused by other classes.

Các lớp khác như

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
41 hoặc
>>> class MyError(Exception):
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
__main__.MyError
93 có thể tái sử dụng
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 mà không liên quan đến
>>> class MyClass:
...     pass
...
51. Họ có thể tận dụng cùng một triển khai đảm bảo rằng các địa chỉ được xử lý nhất quán trên toàn bộ ứng dụng.

Một vấn đề bạn có thể gặp phải khi sử dụng thành phần là một số lớp của bạn có thể bắt đầu phát triển bằng cách sử dụng nhiều thành phần. Các lớp của bạn có thể yêu cầu nhiều tham số trong hàm tạo chỉ để truyền trong các thành phần mà chúng được tạo. Điều này có thể làm cho các lớp học của bạn khó sử dụng.

Một cách để tránh vấn đề là sử dụng phương pháp nhà máy để xây dựng các đối tượng của bạn. Bạn đã làm điều đó với ví dụ thành phần.

Nếu bạn nhìn vào việc triển khai lớp

>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
41, bạn sẽ nhận thấy rằng nó sử dụng
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
51 để xây dựng đối tượng
>>> class MyClass:
...     pass
...
51 với các tham số phù hợp.

Thiết kế này sẽ hoạt động, nhưng lý tưởng nhất, bạn sẽ có thể xây dựng một đối tượng

>>> class MyClass:
...     pass
...
51 chỉ bằng cách chỉ định
>>> class MyClass:
...     pass
...
48, ví dụ
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
01.

Những thay đổi sau đây có thể cải thiện thiết kế của bạn. Bạn có thể bắt đầu với mô -đun

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
34:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
6

Đầu tiên, bạn thực hiện lớp nội bộ

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
03, sau đó cung cấp một biến bên trong
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
04 cho mô -đun. Bạn đang liên lạc với các nhà phát triển khác rằng họ không nên tạo hoặc sử dụng trực tiếp
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
03. Thay vào đó, bạn cung cấp hai chức năng,
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
06 và
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
81, làm giao diện công cộng cho mô -đun. Đây là những gì các mô -đun khác nên sử dụng.

Những gì bạn đang nói là

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
03 là một người độc thân, và chỉ nên có một đối tượng được tạo ra từ nó.

Bây giờ, bạn có thể làm tương tự với mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
7

Một lần nữa, bạn thực hiện nội bộ

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
10 và cung cấp một giao diện công cộng cho nó. Ứng dụng sẽ sử dụng giao diện công cộng để có được chính sách và tính toán bảng lương.

Bây giờ bạn sẽ làm tương tự với mô -đun

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
72:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
8

Về cơ bản, bạn đang nói rằng chỉ nên có một

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
12, một
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
10 và một
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
03. Một lần nữa, mô hình thiết kế này được gọi là mẫu thiết kế Singleton, có ích cho các lớp mà chỉ nên có một, một trường hợp duy nhất.

Bây giờ, bạn có thể làm việc trên mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
70. Bạn cũng sẽ tạo ra một singleton trong số
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
16, nhưng bạn sẽ thực hiện một số thay đổi bổ sung:

# In hr.py

class Employee:
    def __init__(self, id, name):
        self.id = id
        self.name = name
9

Trước tiên, bạn nhập các chức năng và lớp liên quan từ các mô -đun khác.

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
16 được thực hiện nội bộ và ở phía dưới, bạn tạo một thể hiện duy nhất. Trường hợp này là công khai và một phần của giao diện vì bạn sẽ muốn sử dụng nó trong ứng dụng.

Bạn đã thay đổi thuộc tính

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
18 thành một từ điển trong đó khóa là nhân viên
>>> class MyClass:
...     pass
...
48 và giá trị là thông tin của nhân viên. Bạn cũng đã phơi bày một phương thức
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
20 để trả về thông tin cho nhân viên được chỉ định
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
21.

Tài sản

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
22 hiện sắp xếp các khóa để trả lại các nhân viên được sắp xếp theo
>>> class MyClass:
...     pass
...
48 của họ. Bạn đã thay thế phương thức xây dựng các đối tượng
>>> class MyClass:
...     pass
...
51 bằng các cuộc gọi đến trình khởi tạo
>>> class MyClass:
...     pass
...
51 trực tiếp.

Lớp

>>> class MyClass:
...     pass
...
51 hiện được khởi tạo với
>>> class MyClass:
...     pass
...
48 và sử dụng các chức năng công cộng được hiển thị trong các mô -đun khác để khởi tạo các thuộc tính của nó.

Bây giờ bạn có thể thay đổi chương trình để kiểm tra các thay đổi:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
0

Bạn nhập các chức năng liên quan từ các mô -đun

>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21 và
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
34, cũng như lớp
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
30 và
>>> class MyClass:
...     pass
...
51. Chương trình sạch hơn vì bạn tiếp xúc với giao diện cần thiết và đóng gói cách truy cập các đối tượng.

Lưu ý rằng bây giờ bạn có thể tạo một đối tượng

>>> class MyClass:
...     pass
...
51 trực tiếp chỉ bằng cách sử dụng
>>> class MyClass:
...     pass
...
48 của nó. Bạn có thể chạy chương trình để xem đầu ra của nó:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
1

Chương trình hoạt động giống như trước đây, nhưng bây giờ bạn có thể thấy rằng một đối tượng

>>> class MyClass:
...     pass
...
51 có thể được tạo từ
>>> class MyClass:
...     pass
...
48 của nó và hiển thị biểu diễn từ điển của nó.

Hãy xem xét kỹ hơn về lớp

>>> class MyClass:
...     pass
...
51:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
2

Lớp

>>> class MyClass:
...     pass
...
51 là một tổng hợp chứa nhiều đối tượng cung cấp chức năng khác nhau. Nó chứa một
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 thực hiện tất cả các chức năng liên quan đến nơi nhân viên sống.

>>> class MyClass:
...     pass
...
51 cũng chứa vai trò năng suất được cung cấp bởi mô -đun
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
34 và chính sách bảng lương được cung cấp bởi mô -đun
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
21. Hai đối tượng này cung cấp các triển khai được loại bỏ bởi lớp
>>> class MyClass:
...     pass
...
51 để theo dõi công việc theo phương thức
>>> c = MyClass()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__']
88 và để tính toán bảng lương trong phương thức
>>> class MyClass:
...     pass
...
47.

Bạn đang sử dụng sáng tác theo hai cách khác nhau. Lớp

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
58 cung cấp dữ liệu bổ sung cho
>>> class MyClass:
...     pass
...
51 trong đó các đối tượng vai trò và bảng lương cung cấp hành vi bổ sung.

Tuy nhiên, mối quan hệ giữa

>>> class MyClass:
...     pass
...
51 và các đối tượng đó được ghép nối một cách lỏng lẻo, cung cấp một số khả năng thú vị mà bạn sẽ thấy trong phần tiếp theo.

Thành phần để thay đổi hành vi thời gian chạy

Kế thừa, trái ngược với sáng tác, là một mối quan hệ cặp đôi chặt chẽ. Với sự kế thừa, chỉ có một cách để thay đổi và tùy chỉnh hành vi. Ghi đè phương thức là cách duy nhất để tùy chỉnh hành vi của một lớp cơ sở. Điều này tạo ra các thiết kế cứng nhắc khó thay đổi.

Mặt khác, thành phần cung cấp một mối quan hệ được ghép nối lỏng lẻo cho phép thiết kế linh hoạt và có thể được sử dụng để thay đổi hành vi trong thời gian chạy.

Hãy tưởng tượng bạn cần hỗ trợ chính sách khuyết tật dài hạn (LTD) khi tính toán bảng lương. Chính sách tuyên bố rằng một nhân viên trên LTD nên được trả 60% tiền lương hàng tuần của họ với 40 giờ làm việc.

Với một thiết kế kế thừa, đây có thể là một yêu cầu rất khó để hỗ trợ. Thêm nó vào ví dụ thành phần dễ dàng hơn rất nhiều. Hãy bắt đầu bằng cách thêm lớp chính sách:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
3

Lưu ý rằng

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
48 không kế thừa
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06, nhưng thực hiện cùng một giao diện. Điều này là do việc triển khai hoàn toàn khác nhau, vì vậy chúng tôi không muốn kế thừa bất kỳ triển khai
>>> class MyError:
...     pass
...
>>> raise MyError()

Traceback (most recent call last):
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
06 nào.

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
48 khởi tạo
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
52 đến
>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
68 và cung cấp một phương thức
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
54 nội bộ làm tăng ngoại lệ nếu
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
55 chưa được áp dụng. Sau đó, nó cung cấp một phương thức
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
56 để gán
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
52.

Giao diện công cộng trước tiên kiểm tra xem

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
52 đã được áp dụng, và sau đó thực hiện chức năng theo chính sách cơ sở đó. Phương pháp
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
59 chỉ ủy quyền cho chính sách cơ sở và
>>> class MyClass:
...     pass
...
47 sử dụng nó để tính toán
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
61 và sau đó trả lại 60%.

Bây giờ bạn có thể thực hiện một thay đổi nhỏ cho lớp

>>> class MyClass:
...     pass
...
51:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
4

Bạn đã thêm một phương thức

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
63 áp dụng chính sách bảng lương hiện có cho chính sách mới và sau đó thay thế nó. Bây giờ bạn có thể sửa đổi chương trình để áp dụng chính sách cho đối tượng
>>> class MyClass:
...     pass
...
51:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
5

Chương trình truy cập

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
65, được đặt tại Index
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
66, tạo đối tượng
# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
48 và áp dụng chính sách cho nhân viên. Khi
>>> class MyClass:
...     pass
...
47 được gọi, sự thay đổi được phản ánh. Bạn có thể chạy chương trình để đánh giá đầu ra:

# In hr.py

class SalaryEmployee(Employee):
    def __init__(self, id, name, weekly_salary):
        super().__init__(id, name)
        self.weekly_salary = weekly_salary

    def calculate_payroll(self):
        return self.weekly_salary
6

Số tiền kiểm tra cho nhân viên Kevin Bacon, nhân viên bán hàng, hiện có giá $ 1080 thay vì $ 1800. Đó là vì

# In hr.py

class PayrollSystem:
    def calculate_payroll(self, employees):
        print('Calculating Payroll')
        print('===================')
        for employee in employees:
            print(f'Payroll for: {employee.id} - {employee.name}')
            print(f'- Check amount: {employee.calculate_payroll()}')
            print('')
48 đã được áp dụng cho mức lương.

Như bạn có thể thấy, bạn đã có thể hỗ trợ các thay đổi chỉ bằng cách thêm một chính sách mới và sửa đổi một vài giao diện. Đây là loại linh hoạt mà thiết kế chính sách dựa trên thành phần mang lại cho bạn.

Lựa chọn giữa thừa kế và thành phần trong Python

Python, như một ngôn ngữ lập trình định hướng đối tượng, hỗ trợ cả kế thừa và thành phần. Bạn thấy rằng sự kế thừa được sử dụng tốt nhất để mô hình hóa A là một mối quan hệ, trong khi các mô hình thành phần A có mối quan hệ.is a relationship, whereas composition models a has a relationship.

Đôi khi, thật khó để thấy mối quan hệ giữa hai lớp nên là gì, nhưng bạn có thể làm theo các hướng dẫn này:

  • Sử dụng kế thừa trên thành phần trong Python để mô hình hóa một rõ ràng là một mối quan hệ. Đầu tiên, biện minh cho mối quan hệ giữa lớp dẫn xuất và cơ sở của nó. Sau đó, đảo ngược mối quan hệ và cố gắng biện minh cho nó. Nếu bạn có thể biện minh cho mối quan hệ theo cả hai hướng, thì bạn không nên sử dụng kế thừa giữa chúng. to model a clear is a relationship. First, justify the relationship between the derived class and its base. Then, reverse the relationship and try to justify it. If you can justify the relationship in both directions, then you should not use inheritance between them.

  • Sử dụng kế thừa trên thành phần trong Python để tận dụng cả giao diện và thực hiện lớp cơ sở. to leverage both the interface and implementation of the base class.

  • Sử dụng kế thừa trên thành phần trong Python để cung cấp các tính năng mixin cho một số lớp không liên quan khi chỉ có một triển khai của tính năng đó. to provide mixin features to several unrelated classes when there is only one implementation of that feature.

  • Sử dụng thành phần trên sự kế thừa trong Python để mô hình A có mối quan hệ tận dụng việc thực hiện lớp thành phần. to model a has a relationship that leverages the implementation of the component class.

  • Sử dụng thành phần trên sự kế thừa trong Python để tạo ra các thành phần có thể được sử dụng lại bởi nhiều lớp trong các ứng dụng Python của bạn. to create components that can be reused by multiple classes in your Python applications.

  • Sử dụng thành phần trên sự kế thừa trong Python để thực hiện các nhóm hành vi và chính sách có thể được áp dụng thay thế cho các lớp khác để tùy chỉnh hành vi của họ. to implement groups of behaviors and policies that can be applied interchangeably to other classes to customize their behavior.

  • Sử dụng thành phần trên kế thừa trong Python để cho phép thay đổi hành vi thời gian chạy mà không ảnh hưởng đến các lớp hiện có. to enable run-time behavior changes without affecting existing classes.

Sự kết luận

Bạn đã khám phá sự kế thừa và sáng tác trong Python. Bạn đã học về loại mối quan hệ mà sự kế thừa và sáng tác tạo ra. Bạn cũng đã trải qua một loạt các bài tập để hiểu làm thế nào kế thừa và sáng tác được thực hiện trong Python.inheritance and composition in Python. You learned about the type of relationships that inheritance and composition create. You also went through a series of exercises to understand how inheritance and composition are implemented in Python.

Trong bài viết này, bạn đã học được cách:

  • Sử dụng thừa kế để thể hiện mối quan hệ giữa hai lớpis a relationship between two classes
  • Đánh giá xem thừa kế có phải là mối quan hệ đúng không
  • Sử dụng nhiều kế thừa trong Python và đánh giá Python từ MRO để khắc phục sự cố nhiều vấn đề về kế thừa
  • Mở rộng các lớp học với mixin và sử dụng lại việc triển khai của họ
  • Sử dụng thành phần để thể hiện A có mối quan hệ giữa hai lớphas a relationship between two classes
  • Cung cấp các thiết kế linh hoạt bằng cách sử dụng bố cục
  • Tái sử dụng mã hiện có thông qua thiết kế chính sách dựa trên thành phần

Đề xuất đọc

Dưới đây là một số cuốn sách và bài viết khám phá thêm thiết kế theo hướng đối tượng và có thể hữu ích để giúp bạn hiểu được việc sử dụng chính xác sự kế thừa và bố cục trong Python hoặc các ngôn ngữ khác:

  • Các mẫu thiết kế: Các yếu tố của phần mềm hướng đối tượng có thể tái sử dụng
  • Đầu thiết kế đầu tiên: Hướng dẫn thân thiện với não
  • Mã sạch: Cẩm nang về sự khéo léo của phần mềm Agile
  • Nguyên tắc vững chắc
  • Nguyên tắc thay thế Liskov

Xem bây giờ hướng dẫn này có một khóa học video liên quan được tạo bởi nhóm Python thực sự. Xem nó cùng với hướng dẫn bằng văn bản để làm sâu sắc thêm sự hiểu biết của bạn: Kế thừa và sáng tác: Hướng dẫn Python OOP This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Inheritance and Composition: A Python OOP Guide