Kế thừa đa cấp trong Python W3schools

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
  • Use multiple inheritance in Python and understand its drawbacks
  • Use composition to create complex objects
  • Reuse existing code by applying composition
  • Change application behavior at run-time through composition

Free Bonus. Click here to get access to a free Python OOP Cheat Sheet that points you to the best tutorials, videos, and books to learn more about Object-Oriented Programming with Python

What Are Inheritance and Composition?

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

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

Remove ads

What’s Inheritance?

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

# 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
6 class that inherits from a
# 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
7 class, you created a relationship where
# 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
6 is a specialized version 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
7

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

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

Ghi chú. In an inheritance relationship

  • Classes that inherit from another are called derived classes, subclasses, or subtypes
  • Classes from which other classes are derived are called base classes or super classes
  • A derived class is said to derive, inherit, or extend a base class

Let’s say you have a base class

# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
0 and you derive from it to create a
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 class. The inheritance relationship states that a
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 is an
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
0. This means that
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 inherits the interface and implementation of
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
0, and
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 objects can be used to replace
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
0 objects in the application

This is known as the Liskov substitution principle. The principle states that “in a computer program, if

# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
8 is a subtype of
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
9, then objects of type
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
9 may be replaced with objects of type
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
8 without altering any of the desired properties of the program”

You’ll see in this article why you should always follow the Liskov substitution principle when creating your class hierarchies, and the problems you’ll run into if you don’t

What’s Composition?

Composition is a concept that models a has a relationship. Nó cho phép tạo các kiểu phức tạp bằng cách kết hợp các đối tượng thuộc các kiểu khác. This means that a class

>>> 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__']
02 can contain an object of another class
>>> 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__']
03. Mối quan hệ này có nghĩa là mộ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__']
02 có mộ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__']
03

UML đại diện cho thành phần như sau

Composition is represented through a line with a diamond at the composite class pointing to the component class. The composite side can express the cardinality of the relationship. The cardinality indicates the number or valid range of

>>> 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__']
03 instances 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__']
02 class will contain

In the diagram above, 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__']
08 represents that 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__']
02 class contains one object of type
>>> 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__']
03. Cardinality can be expressed in the following ways

  • A number indicates the number of
    >>> 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__']
    
    03 instances that are contained in 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__']
    
    02
  • The * symbol indicates that 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__']
    
    02 class can contain a variable number of
    >>> 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__']
    
    03 instances
  • A range 1. 4 indicates that 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__']
    
    02 class can contain a range of
    >>> 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__']
    
    03 instances. The range is indicated with the minimum and maximum number of instances, or minimum and many instances like in 1. *

Note. Classes that contain objects of other classes are usually referred to as composites, where classes that are used to create more complex types are referred to as components

For example, your

# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 class can be composed by another object of type
>>> 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__']
18. Composition allows you to express that relationship by saying a
# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 has 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__']
18

Composition enables you to reuse code by adding objects to other objects, as opposed to inheriting the interface and implementation of other classes. Both

# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission
1 and
>>> 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__']
22 classes can leverage the functionality of
>>> 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__']
18 through composition without deriving one class from the other

An Overview of Inheritance in Python

Everything in Python is an object. Modules are objects, class definitions and functions are objects, and of course, objects created from classes are objects too

Inheritance is a required feature of every object oriented programming language. This means that Python supports inheritance, and as you’ll see later, it’s one of the few languages that supports multiple inheritance

When you write Python code using classes, you are using inheritance even if you don’t know you’re using it. Let’s take a look at what that means

Remove ads

The Object Super Class

The easiest way to see inheritance in Python is to jump into the and write a little bit of code. You’ll start by writing the simplest class possible

>>>

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

You declared a class

>>> 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__']
24 that doesn’t do much, but it will illustrate the most basic inheritance concepts. Now that you have the class declared, you can use 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__']
25 function to list its members

>>>

>>> 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__']

returns a list of all the members in the specified object. You have not declared any members in

>>> 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__']
24, so where is the list coming from? You can find out using the interactive interpreter

>>>

>>> 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__']

As you can see, the two lists are nearly identical. There are some additional members in

>>> 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__']
24 like
>>> 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__']
29 and
>>> 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__']
30, but every single member 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__']
31 class is also present in
>>> 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__']
24

This is because every class you create in Python implicitly derives 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__']
31. You could be more explicit and write
>>> 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__']
34, but it’s redundant and unnecessary

Note. In Python 2, you have to explicitly 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__']
31 for reasons beyond the scope of this article, but you can read about it in the section of the Python 2 documentation

Exceptions Are an Exception

Every class that you create in Python will implicitly 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__']
31. The exception to this rule are classes used to indicate errors by raising an exception

You can see the problem using the Python interactive interpreter

>>>

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

Bạn đã tạo một lớp mới để chỉ ra một loại lỗi. Then you tried to use it to raise an exception. An exception is raised but the output states that the exception is of type

>>> 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__']
37 not
>>> 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__']
38 and that all
>>> 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__']
39

>>> 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__']
40 is a base class provided for all error types. To create a new error type, you must derive your class 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__']
40 or one of its derived classes. The convention in Python is to derive your custom error types 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__']
42, which in turn derives 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__']
40

The correct way to define your error type is the following

>>>

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError

As you can see, when you raise

>>> 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__']
38, the output correctly states the type of error raised

Creating Class Hierarchies

Inheritance is the mechanism you’ll use to create hierarchies of related classes. These related classes will share a common interface that will be defined in the base classes. Derived classes can specialize the interface by providing a particular implementation where applies

In this section, you’ll start modeling an HR system. The example will demonstrate the use of inheritance and how derived classes can provide a concrete implementation of the base class interface

The HR system needs to process payroll for the company’s employees, but there are different types of employees depending on how their payroll is calculated

Bạn bắt đầu bằng cách triển khai 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__']
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['']

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__']
45 implements 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__']
47 method that takes a collection of employees and prints their
>>> 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__']
48,
>>> 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__']
49, and check amount using 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__']
47 method exposed on each employee object

Now, you implement a base class

>>> 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__']
51 that handles the common interface for every employee type

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name

>>> 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__']
51 is the base class for all employee types. It is constructed with an
>>> 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__']
48 and 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__']
49. What you are saying is that every
>>> 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__']
51 must have an
>>> 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__']
48 assigned as well as a name

The HR system requires that every

>>> 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__']
51 processed must provide 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__']
47 interface that returns the weekly salary for the employee. The implementation of that interface differs depending on the type of
>>> 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__']
51

For example, administrative workers have a fixed salary, so every week they get paid the same amount

# 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

You create a derived class

>>> 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__']
60 that inherits
>>> 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__']
51. The class is initialized with 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__']
48 and
>>> 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__']
49 required by the base class, and you use
>>> 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__']
64 to initialize the members of the base class. You can read all about
>>> 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__']
64 in Supercharge Your Classes With Python super[]

>>> 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__']
60 also requires 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__']
67 initialization parameter that represents the amount the employee makes per week

The class provides the required

>>> 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__']
47 method used by the HR system. The implementation just returns the amount stored in
>>> 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__']
67

The company also employs manufacturing workers that are paid by the hour, so you add an

>>> 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 to the HR system

# 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

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__']
70 class is initialized with
>>> 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__']
48 and
>>> 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__']
49, like the base class, plus 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__']
74 and 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__']
75 required to calculate the payroll. 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__']
47 method is implemented by returning the hours worked times the hour rate

Finally, the company employs sales associates that are paid through a fixed salary plus a commission based on their sales, so you create 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__']
77 class

# In hr.py

class CommissionEmployee[SalaryEmployee]:
    def __init__[self, id, name, weekly_salary, commission]:
        super[].__init__[id, name, weekly_salary]
        self.commission = commission

    def calculate_payroll[self]:
        fixed = super[].calculate_payroll[]
        return fixed + self.commission

You derive

>>> 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 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__']
60 because both classes have 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__']
67 to consider. At the same time,
>>> 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 is initialized with 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 value that is based on the sales for the employee

>>> 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__']
47 leverages the implementation of the base class to retrieve 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__']
84 salary and adds the commission value

Since

>>> 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 derives 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__']
60, you have access to 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__']
67 property directly, and you could’ve implemented
>>> 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__']
47 using the value of that property

The problem with accessing the property directly is that if the implementation of

>>> 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__']
89 changes, then you’ll have to also change the implementation of
>>> 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__']
90. It’s better to rely on the already implemented method in the base class and extend the functionality as needed

You created your first class hierarchy for the system. The UML diagram of the classes looks like this

The diagram shows the inheritance hierarchy of the classes. The derived classes implement 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__']
91 interface, which is required by 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__']
45. 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__']
93 implementation requires that 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__']
94 objects passed contain an
>>> 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__']
48,
>>> 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__']
49, and
>>> 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__']
97 implementation

Interfaces are represented similarly to classes with the word interface above the interface name. Interface names are usually prefixed with a capital

>>> 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__']
98

The application creates its employees and passes them to the payroll system to process payroll

>>> 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

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

>>> 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

The program creates three employee objects, one for each of the derived classes. Sau đó, nó tạo hệ thống tính lương và chuyển danh sách nhân viên sang 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__']
47 của nó, tính toán bảng lương cho từng nhân viên và in kết quả

Notice how 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__']
51 base class doesn’t define 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__']
47 method. This means that if you were to create a plain
>>> 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__']
51 object and pass it to 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__']
45, then you’d get an error. Bạn có thể dùng thử trong trình thông dịch tương tác Python

>>>

>>> 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__']
2

Mặc dù bạn có thể khởi tạo 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__']
51 nhưng đối tượng đó không thể đượ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__']
45. Tại sao? . Để đáp ứng các yêu cầ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__']
45, bạn sẽ muốn chuyển đổ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__']
51, hiện là lớp cụ thể, thành lớp trừu tượng. Bằng cách đó, không có nhân viên nào chỉ là mộ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__']
51, mà là một người thực hiệ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__']
47

Remove ads

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

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__']
51 trong ví dụ trên đượ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ờ được khởi tạo. Python cung cấp 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__']
13 để định nghĩa các lớp cơ sở trừu tượng

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

Thư viện chuẩn trong Python cung cấp chức năng ngă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

>>> 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__']
51 để đảm bảo rằng nó không thể được 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__']
3

Bạn lấy đượ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__']
51 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__']
17, biến nó thành một lớp cơ sở trừu tượng. Sau đó, bạn trang trí 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__']
47 với trình trang trí
>>> 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__']
19

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

  1. Bạn đang nói với người dùng của mô-đun rằng không thể tạo các đối tượng thuộc loạ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__']
    
    51
  2. Bạn đang nói với các nhà phát triển khác đang làm việc trê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__']
    
    21 rằng nếu họ 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__']
    
    51, thì họ phải ghi đè phương thức trừu tượ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__']
    
    47

Bạn có thể thấy rằng các đối tượng thuộc loạ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__']
51 không thể được tạo bằng trình thông dịch tương 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__']
4

Đầu ra cho thấy rằng lớp không thể được khởi tạo vì nó chứa một phương thức trừu 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__']
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 triển khai so với Kế thừa giao diện

Khi bạn dẫn xuất một lớp này từ một lớp khác, lớp dẫn xuất sẽ 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ở

  2. Việc thực hiện lớp cơ sở. Lớp dẫn xuất kế thừa mã cài đặt giao diện lớp

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 triển khai nhiều giao diện, để 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

Các ngôn ngữ lập trình hiện đại được thiết kế dựa trên khái niệm cơ bản này. 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 cần phải khai báo giao diện một cách rõ ràng. Bất kỳ đối tượng nào triển khai giao diện mong muốn đều có thể được sử dụng thay cho đối tượng khác. Điều này được gọi là. Gõ vịt thường được giải thích là “nếu nó hoạt động giống như một con vịt, thì đó là một con vịt. ”

Để minh họa điều này, bây giờ bạn sẽ thêm một 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__']
26 vào ví dụ trên mà lớp này không xuất phát 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__']
51

>>> 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

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__']
26 không xuất phát 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__']
51, nhưng nó hiển thị cùng một giao diện được yêu cầu 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__']
45.
>>> 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__']
93 yêu cầu một danh sách các đối tượng triển khai giao diện sau

  • Thuộc tính hoặc thuộc tí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__']
    
    48 trả về id của nhân viên
  • Thuộc tính hoặc thuộc tí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__']
    
    49 đại diện cho tên của nhân viên
  • Một 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__']
    
    47 không nhận bất kỳ tham số nào và trả về số tiền lương để xử lý

Tất cả những yêu cầu này đều được 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__']
26 đáp ứng, vì vậy 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__']
45 vẫn có thể tính bảng lương của mình

Bạn có thể sửa đổi chương trình để sử dụng 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__']
26

>>> 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 tạo một đố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__']
26 và thêm nó vào danh sách được xử lý 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__']
45. Bây giờ bạn có thể chạy chương trình và xem đầu ra 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__']
7

Như bạn có thể thấ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__']
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 cần phải xuất phát từ một lớp cụ thể để chương trình có thể sử dụng lại các đối tượng của bạn, bạn có thể hỏi tại sao bạn nên sử dụng tính kế thừa thay vì chỉ triển khai giao diện mong muốn. Các quy tắc sau đây 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 triển khai lớp cơ sở của chúng. Họ cũng phải mô hình hóa một mối quan hệ. Một 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__']
    
    41 cũng có thể có 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__']
    
    48 và 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__']
    
    49, nhưng một 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__']
    
    41 không phải là 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__']
    
    51, vì vậy bạn không nên sử dụng thừa kế

  • Thực hiện một giao diện đượ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ể của ứng dụng, bạn triển khai giao diện bắt buộc trong lớp của mình, nhưng bạn không cần cung cấp lớp cơ sở hoặc kế thừa từ lớp khác

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

>>> 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__']
46 rồi sửa đổ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__']
21 về trạng thái ban đầu

>>> 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 đã loại bỏ việc nhập 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__']
13 vì 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__']
51 không cần phải trừu tượng. Bạn cũng đã xóa phương thức trừu 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__']
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 triển khai các thuộc tí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__']
48 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__']
49 của 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__']
51 trong các lớp dẫn xuất của bạn. 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__']
47 chỉ là một giao diện củ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__']
93, nên bạn không cần triển khai nó trong lớp cơ sở
>>> 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__']
51

Lưu ý cách 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__']
77 bắt nguồn 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__']
60. Điều này có nghĩa là
>>> 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 kế thừa việc triển khai và giao diện 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__']
60. Bạn có thể thấy 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__']
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ừ
>>> 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 để triển khai phiên bản của chính nó

Remove ads

Vấn đề bùng nổ lớp học

Nếu bạn không cẩn thận, tính 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à khó duy trì. Đây được gọi là vấn đề bùng nổ lớp

Bạn đã bắt đầu xây dựng hệ thống phân cấp lớp gồ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__']
51 loại đượ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__']
45 để tính 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 đó để chúng có thể được sử dụng 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__']
65 mớ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__']
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

  • quản lý. Họ đi xung quanh và la mắng mọi người bảo họ phải làm gì. Họ là những người làm công ăn lương và kiếm được nhiều tiền hơn
  • thư ký. Họ làm tất cả các công việc giấy tờ cho người 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à người làm công ăn lương nhưng kiếm được ít tiền hơn
  • nhân viên kinh doanh. Họ thực hiện rất nhiều cuộc điện thoại để bán sản phẩm. Họ có lương, nhưng họ cũng nhận được hoa hồng khi bán hàng
  • Công nhân nhà máy. Họ sản xuất các sản phẩm cho công ty. Họ được trả lương theo giờ

Với những yêu cầu đó, bạn bắt đầu thấy 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__']
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 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__']
21 bởi vì bây giờ chúng cũng được sử dụng bở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__']
65

Bạn tạo một 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__']
70 và di chuyển các lớp đế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

Việc triển khai vẫn giữ nguyên, nhưng bạn chuyển các lớp sang 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__']
94. Bây giờ, bạn thay đổi chương trình của mình để hỗ trợ thay đổ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__']
0

Bạn chạy chương trình và xác minh rằng nó vẫn hoạ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__']
1

Với mọi thứ đã sẵn sàng, bạn bắt đầu thêm các lớp mớ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__']
2

Đầu tiên, bạn thêm một 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__']
72 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__']
60. Lớp tiết lộ một 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__']
74 sẽ được sử dụng bởi hệ thống năng suất. Phương pháp lấy
>>> 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__']
75 nhân viên đã làm việc

Sau đó, bạn 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__']
76,
>>> 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__']
77 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__']
78 rồi triển khai giao diệ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__']
74 để hệ thống năng suất có thể sử dụng chúng

Bây giờ, bạn có thể thêm 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__']
80

>>> 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

Lớp theo dõi nhân viên theo 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__']
81 lấy danh sách nhân viên và số giờ cần 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

>>> 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

Chương trình tạo danh sách nhân viên các loại. 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 lương cho họ

Bạn có thể chạy chương trình để xem đầu 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__']
5

Chương trình hiển thị 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 từng 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ác yêu cầu mới, 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 đề bùng nổ 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à khó duy trì

Sơ đồ 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 về số lượng lớp học với thiết kế này

Remove ads

Kế thừa nhiều lớp

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

Đa kế thừa có tiếng xấu đến mức hầu hết các ngôn ngữ lập trình hiện đại không hỗ trợ 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 đó triển khai nhiều giao diện, vì vậy 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 ra 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 triển khai một lớp bằng cách xuất phát trực tiếp từ nó. Bạn có thể triển khai nhiều giao diện, nhưng bạn không thể kế thừa việc triển khai nhiều lớp

Ràng buộc 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 của mình với ít phụ thuộc lẫn nhau hơn. Ở phần sau của bài viết này, bạn sẽ thấy rằng bạn có thể tận dụng nhiều triển khai thông qua thành phần, giúp phần mềm trở nên linh hoạt hơn. Tuy nhiên, phần này nói về đa thừa kế, vì vậy chúng ta hãy xem nó hoạt động như thế nào

Hóa ra đôi khi thư ký tạm thời được thuê khi có quá nhiều giấy tờ phải làm. 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__']
82 thực hiện vai trò của 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__']
76 trong ngữ cảnh của 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__']
65, nhưng đối với mục đích trả lương, nó là 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__']
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 lựa chọn

  1. Bắt nguồn 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__']
    
    76. Bạn có thể bắt nguồn 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__']
    
    76 để kế thừa 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__']
    
    88 cho vai trò, 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__']
    
    47 để triển khai 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__']
    
    70

  2. Xuất phát 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__']
    
    70. Bạn có thể 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__']
    
    70 để 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__']
    
    47, sau đó ghi đè 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__']
    
    88 để triển khai nó dưới 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__']
    
    76

Sau đó, bạn nhớ rằng Python hỗ trợ đa kế thừa, vì vậy bạn quyết định xuất phát 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__']
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__']
70

>>> 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

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

>>> 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

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

>>> 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

Bạn nhận được một ngoại lệ nói 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__']
99 đối số vị trí được mong đợi, nhưng
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
00 đã được đưa ra

Điều này là do bạn bắt nguồn 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__']
82 đầu tiên 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__']
76 và sau đó 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__']
70, vì vậy trình thông dịch đang cố gắng sử dụng
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
04 để khởi tạo đối tượng

Được rồi, hãy đả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__']
9

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

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

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

>>> 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__']
67, tham số này cần thiết để khởi tạo
>>> 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__']
76, nhưng tham số đó không có ý nghĩa trong ngữ cảnh củ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__']
82 bởi vì nó là mộ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__']
70

Có thể thực hiện

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
09 sẽ giúp ích

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

Thử nó

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

Điều đó cũng không hiệu quả. Được rồi, đã đến lúc bạn đi sâu vào trình tự giải quyết phương pháp Python [MRO] để xem điều gì đang xảy ra

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 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__']
64 để xác định phương thức hoặc thuộc tính nào sẽ gọi. Bạn có thể tìm hiểu thêm 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__']
64 trong Supercharge Your Classs With Python super[]

Bạn có thể đánh giá MRO của 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__']
82 bằng 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
3

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ụ này, đây là điều xảy ra khi chúng ta tạo đố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__']
82

  1. Phương pháp

    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    14 được gọi là

  2. Cuộc gọi

    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    15 phù hợp với
    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    16

  3. >>> 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 cuộc gọi
    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    18, mà MRO sẽ khớp với
    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    04, được kế thừa từ
    >>> class MyError:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    TypeError: exceptions must derive from BaseException
    
    20

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

>>> 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__']
37 được đưa ra

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

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
22 như sau

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

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

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

Vấn đề bây giờ là vì bạn đã đảo ngược thứ tự thừa kế, MRO đang tìm 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__']
47 của
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
24 trước phương pháp 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__']
70. Bạn cần ghi đè
>>> 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__']
47 trong
>>> 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 và gọi triển khai đúng từ nó

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

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__']
97 gọi trực tiếp
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
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 để thấy nó hoạt động

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

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

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

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

Sơ đồ cho thấy vấn đề kim cương với thiết kế lớp hiện tạ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__']
82 sử dụng đa thừa kế để dẫn xuất từ ​​hai lớp mà cuối cùng cũng dẫn xuất 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__']
51. Điều này khiến hai đường dẫn đến lớp cơ sở
>>> 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__']
51, đây là điều bạn muốn tránh trong thiết kế của mình

Vấn đề kim cương xuất hiện khi bạn đang 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ể gây ra phiên bản sai của một phương thức được gọi

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

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

Các lớp dẫn xuấ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__']
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

  2. Hệ thống bảng lương tính toán bảng lương của nhân viên

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

>>> class MyError:
..     pass
...
>>> raise MyError[]

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

Mô-đun

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
34 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__']
65, cũng như các vai trò liên quan mà nó hỗ trợ. Các lớp triển khai giao diệ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__']
74 theo yêu cầu của hệ thống, nhưng chúng không 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__']
51

Bạn có thể 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__']
21

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
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__']
21 thực hiệ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__']
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 không 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__']
51 nữa

Bây giờ bạn có thể thêm các lớp cần thiết vào 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__']
94

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

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__']
70 nhập các chính sách và vai trò từ các mô-đun khác và triển khai các loạ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__']
51 khác nhau. Bạn vẫn đang sử dụng đa kế thừa để kế thừa việc triển khai các lớp chính sách tiền lương và vai trò năng suất, nhưng việc triển khai từng lớp chỉ cần xử lý 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 tiền lương trong hàm tạo. Bạn có thể thấy rằng phần khởi tạo 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__']
72 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__']
76 giống hệt nhau. Ngoài ra, phần khởi tạo 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__']
78 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 giống nhau

Bạn sẽ không muốn có kiểu 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ế hệ thống phân cấp lớp

Đây là sơ đồ UML cho thiết kế mới

Sơ đồ hiển thị các mối quan hệ để xá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__']
76 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 bằng cách sử dụng đa thừa kế, nhưng tránh vấn đề kim cương

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
1

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

Remove ads

Thành phần trong Python

Thành phần là một khái niệm thiết kế hướng đối tượng mà mô hình có một mối quan hệ. Trong thành phần, một lớp được gọi là hỗn 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 lớp khác

Thành phần cho phép các lớp tổng hợp sử dụng lại việc triển khai 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à liên kết lỏng lẻo. Điều đó có nghĩa là những 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à những thay đổi đối với 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 đưa ra 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 tính 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ể xem cách sáng tác hoạt động

Bạn đã sử dụng bố cục trong các ví dụ của chúng tôi. Nếu bạn nhìn vào 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__']
51, bạn sẽ thấy rằng nó chứa hai thuộc tính

  1. >>> 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__']
    
    48 để xác định nhân viên
  2. >>> 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__']
    
    49 để chứa tên của nhân viên

Hai thuộc tính này là các đối tượng 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__']
51 có. Do đó, bạn có thể nói rằng mộ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__']
51 có mộ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__']
48 và có một tên

Một thuộc tính khác cho mộ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__']
51 có thể là một
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

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 đã đặ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
59 là tùy chọn vì không phải địa chỉ nào cũng có thành phần đó

Bạn đã triển khai

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
60 để cung cấp một biểu diễn đẹp mắt của một
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58. Bạn có thể thấy cách triển khai này trong trình thông dịch tương tác

>>>

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

Khi bạn

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
62 biến
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
63, phương thức đặc biệt
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
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ỉ, nên bạn sẽ có một biểu diễn đẹp, dễ đọc. Quá tải toán tử và 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 về các phương thức đặc biệt có sẵn trong các lớp có thể được triển khai để 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

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 vào 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__']
51 thông qua thành phần

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

Bây giờ, bạn khởi tạo thuộc tính

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
63 thành
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
68 để làm cho nó trở thành tùy chọn, nhưng bằng cách đó, giờ đây bạn có thể gán một
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 cho mộ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__']
51. Cũng lưu ý rằng không có tham chiếu nào trong 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__']
94 đến mô-đun
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
72

Thành phần là một mối quan hệ kết hợp lỏng lẻo 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 thể hiện mối quan hệ giữ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__']
51 và
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 trông như thế này

Sơ đồ cho thấy mối quan hệ thành phần cơ bản giữ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__']
51 và
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58

Bây giờ bạn có thể sửa đổ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__']
45 để tận dụng thuộc tính
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
63 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__']
51

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

Bạn kiểm tra xem đố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__']
94 có địa chỉ không, nếu có thì in ra. 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[Exception]:
..     pass
...
>>> raise MyError[]

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

Bạn đã thêm một vài địa chỉ vào 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
81 và
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
82. Khi bạn chạy chương trình, bạn sẽ thấy các địa chỉ được in

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

Lưu ý cách đầu ra bảng lương cho 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
81 và
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
82 hiển thị địa chỉ nơi séc được gử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__']
51 tận dụng 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
58 mà không có bất kỳ kiến ​​thức nào về đối tượng
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 là gì hoặc nó được biểu diễn như thế nào. Kiểu thiết kế này linh hoạt đến mức bạn có thể thay đổi lớp
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 mà không ảnh hưởng đế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__']
51

Remove ads

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

Thành phần linh hoạt hơn thừa kế vì nó mô hình hóa mối quan hệ kết hợp lỏng lẻo. Các thay đổi đối với một lớp thành phần có ảnh hưởng tối thiểu hoặc không ảnh hưởng đến lớp tổng hợp. Thiết kế dựa trên thành phần 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 triển khai 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ụ đa thừa kế ở trên. Hãy tưởng tượng chính sách tiền lương mới sẽ ảnh hưởng đến thiết kế như thế nào. Cố gắng hình dung hệ thống phân cấp lớp sẽ như thế nào nếu cần có vai trò mới. Như bạn đã thấy trước đây, việc phụ thuộc quá nhiều vào tính kế thừa có thể dẫn đến bùng 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 nhiều như thế nào, mà là mối quan hệ giữa các lớp đó chặt chẽ đến mức nào. Các lớp liên kết chặt chẽ ảnh hưởng lẫn nhau khi các thay đổi được đưa ra

Trong phần này, bạn sẽ sử dụng bố cục để thực hiện một thiết kế tốt hơn mà vẫn phù hợp với các yêu cầ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__']
45 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__']
65

Bạn có thể bắt đầu bằng cách triển khai chức năng 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__']
65

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

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

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__']
65 xác định một số vai trò bằng cách sử dụng mã định danh chuỗi được ánh xạ tới lớp vai trò thực hiện vai trò đó. Nó hiển thị 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
94, được cung cấp 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ì 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
95 sẽ được đưa ra

Nó cũng hiển thị chức năng trước đó trong phương pháp

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
96, trong đó đưa ra một 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ể triển khai 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
9

Mỗi vai trò mà bạn đã triển khai đều cho thấy một

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
97 chiếm số lượ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__']
75 đã làm việc. Các phương thức 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 hiển thị cùng một giao diện, vì vậy chúng có thể hoán đổi cho nhau. Sau này bạn sẽ thấy chúng được sử dụng như thế nào trong ứng dụng

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

>>> 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__']
45 cho ứng dụ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['']
0

>>> 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__']
45 lưu giữ cơ sở dữ liệu nội bộ về các chính sách trả lương cho mỗi nhân viên. Nó cho thấy một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
01, được cung cấp cho một nhân viê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__']
48, trả về chính sách trả lương của nó. Nếu mộ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__']
48 được chỉ định không tồn tại trong hệ thống, thì phương thức này sẽ tạo ra 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
95

Việc triển khai

>>> 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__']
47 hoạt động giống như trước đây. Nó lấy danh sách nhân viên, tính 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 trả 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['']
1

Trước tiên, bạn triển khai lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
06 đóng vai trò là lớp cơ sở cho tất cả các chính sách trả lương. Lớp này theo dõ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__']
74, phổ biến cho tất cả các chính sách trả lương

Các lớp chính sách khác bắt nguồn từ

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
06. Chúng tôi sử dụng tính kế thừa ở đây vì chúng tôi muố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
06. Ngoài ra,
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
10,
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
11, và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
12 là một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
06

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
10 được khởi tạo với giá 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__']
67 sau đó được sử dụng 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__']
47.
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
11 được khởi tạo 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__']
75 và triển khai
>>> 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__']
47 bằng cách tận dụng lớp cơ sở
>>> 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

Lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
12 bắt nguồn từ lớp
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
10 vì nó muốn kế thừa việc thực hiện của nó. Nó được khởi tạo với tham số
>>> 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__']
67, nhưng nó cũng yêu cầu tham số
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
24

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
24 được sử dụng để tính toán
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
26, được triển khai như một thuộc tính để nó được tính toán khi được yêu cầu. Trong ví dụ này, chúng tôi giả định rằng một lần bán hàng diễn ra sau mỗi 5 giờ làm việc và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
26 là số lần bán hàng nhân với giá trị
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
24

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
12 triển khai 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__']
47 bằng cách trước tiên tận dụng việc triển khai trong
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
10 và sau đó thêm hoa hồng đã tính

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
32 để quản lý địa chỉ nhân viê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['']
2

Lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
32 giữ một cơ sở dữ liệu nội bộ gồm 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
58 cho mỗi nhân viên. Nó hiển thị một phương thức
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
35 trả về địa chỉ của nhân viên được chỉ đị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__']
48. Nếu nhân viê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__']
48 không tồn tại, thì nó sẽ tăng
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
95

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
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
2

Lớp quản lý các thành phần địa chỉ và cung cấp một biểu diễn đẹp về đị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

>>> 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__']
70 và các lớp của nó

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
41

# 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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
41 theo dõi tất cả nhân viên trong công ty. Đối với mỗi nhân viên, nó theo dõ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__']
48,
>>> 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__']
49 và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
45. Nó có một phiên bả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__']
65,
>>> 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__']
45 và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
32. Những trường hợp này được sử dụng để tạo nhân viên

Nó hiển thị một thuộc tính

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
49 trả về danh sách nhân viên. Các đố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__']
51 được tạo trong một phương thức nội bộ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
51. Lưu ý rằng bạn không có các loại lớp họ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__']
51 khác nhau. Bạn chỉ cần triển khai 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__']
51 duy nhấ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['']
5

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__']
51 được khởi tạo với các thuộc tí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__']
48,
>>> 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__']
49 và
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
63. Nó cũng đòi hỏi năng suất
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
45 của nhân viên và chính sách
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
59

Lớp đưa ra một 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__']
88 mất nhiều giờ làm việc. Phương pháp này trước tiên truy xuất
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
61 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
45. Nói cách khác, nó ủy quyền cho đối tượng
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
45 để thực hiện nhiệm vụ của mình

Theo cách tương tự, nó ủy quyền cho đối tượng

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
59 để theo dõi công việ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__']
75.
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
59, như bạn đã thấy, sử dụng số giờ đó để tính bảng lương nếu cần

Sơ đồ 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

>>> 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__']
51 duy nhất bao gồm các đối tượng dữ liệu khác như
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 và phụ thuộc vào giao diện
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
69 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__']
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

# 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 có thể chạy chương trình để xem đầu ra của 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['']
7

Thiết kế này được gọi là , trong đó các lớp bao gồm các chính sách và chúng ủy quyền 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 Modern C++ Design và nó sử dụng siêu lập trình 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 bố cục, như bạn đã thấy trong ví dụ trên

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

Remove ads

Tùy chỉnh hành vi với thành phần

Nếu thiết kế của bạn dựa trên sự 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 bố cục, 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

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
81 của chúng ta đột nhiên trở thành nhân viên tạm thời được trả lương 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

# 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

Chương trình lấy danh sách nhân viên từ

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
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 một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
11 mới được khởi tạo ở mức $55 mỗi giờ và gán nó cho đối tượng người quản lý

Chính sách mới hiện đượ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__']
45 để sửa đổi hành vi hiện tại. 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['']
9

Tấm séc cho 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 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 hiện có nào. Xem xét loại thay đổi nào sẽ được yêu cầu với thiết kế kế thừa

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

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

Cho đến giờ, bạn đã thấy cách hoạt động của tính kế thừa và thành phần trong Python. Bạn đã thấy rằng các lớp dẫn xuất kế thừa giao diện và triển khai của các lớp cơ sở của chúng. Bạn cũng đã thấy rằng thành phần cho phép bạn sử dụng lại việc triển khai một lớp khác

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

Bạn cũng đã thấy rằng cách gõ vịt của 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 của bạn được sử dụng lại

Tại thời điểm này, bạn có thể hỏi khi nào nên sử dụng thừa kế so với thành phần trong Python. Cả hai đều cho phép sử dụng lại mã. Kế thừa và thành phần có thể giải quyết các vấn đề tương tự trong 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ó lúc việc kế thừa 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 mô hình Mối quan hệ “Là A”

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

# 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
6, kế thừa từ
# 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
7, có thể thay thế một đối tượng kiểu
# 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
7 mà không làm thay đổi các thuộc tính mong muốn của chương trình

Nguyên tắc thay thế Liskov là hướng dẫn quan trọng nhất để xác định xem 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 bài kiểm tra đơn giản mà bạn có thể sử dụng để xác định xem thiết kế của mình có tuân theo nguyên tắc thay thế của Liskov hay không

Giả sử bạn có một lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
78 cung cấp cách 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[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
79. Suy nghĩ ban đầu của bạn là bạn có thể lấy
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
79 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
78 và kế thừa cả giao diện và triển khai. Để chắc chắn đây là thiết kế phù hợp, bạn làm theo các bước sau

  1. Đánh giá

    >>> class MyError[Exception]:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    __main__.MyError
    
    79 là một
    >>> class MyError[Exception]:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    __main__.MyError
    
    78. Hãy suy nghĩ về mối quan hệ này và biện minh cho nó. Liệu nó có ý nghĩa?

  2. Đánh giá

    >>> class MyError[Exception]:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    __main__.MyError
    
    78 là một
    >>> class MyError[Exception]:
    ..     pass
    ...
    >>> raise MyError[]
    
    Traceback [most recent call last]:
      File "", line 1, in 
    __main__.MyError
    
    79. Đảo ngược mối quan hệ và biện minh cho nó. Liệu nó cũng có ý nghĩa?

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 kế thừa các lớp đó từ lớp khác. Hãy xem xét một ví dụ cụ thể hơn

Bạn có một lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 hiển thị một thuộc tính
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
87. Bạn cần một lớp
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88, lớp này cũng có một lớp
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
87. Có vẻ như
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 là một loại đặc biệt của
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86, vì vậy có lẽ bạn có thể rút ra từ nó và tận dụng cả giao diện và triển khai

Trước khi bắt đầu triển khai, bạn sử dụng nguyên tắc thay thế Liskov để đánh giá mối quan hệ

Một

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 là một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 vì diện tích của nó được tính từ tích của ____61_______94 nhân với ____61_______95 của nó. Ràng buộc là
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
96 và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
97 phải bằng nhau

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 lại là một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86. Hãy đảo ngược mối quan hệ để xem nó có hợp lý không

Một

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 là một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 vì diện tích của nó được tính từ tích của ____61_______94 nhân với ____61_______95 của nó. Sự khác biệt 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['']
04 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['']
05 có thể thay đổi độc lập

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 rằng hai lớp này sẽ không bao giờ xuất phát từ nhau

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 để giải thích quyền thừa kế. Bạn có thể hoài nghi với bài kiểm tra nhỏ 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 dẫn xuất
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86

Đầu tiên, bạn triển khai

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86. Bạn thậm chí sẽ đóng gói các thuộc tính để đảm bảo rằng tất cả các ràng buộc đều được đáp ứng

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
0

Lớp

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 được khởi tạo với một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
95 và một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
94, và nó cung cấp một thuộc tính
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
87 trả về diện tích.
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
95 và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
94 được đóng gói để tránh thay đổi chúng trực tiếp

Bây giờ, bạn lấy được

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 và ghi đè giao diện cần thiết để đáp ứng các ràng buộc của một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88

# 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
88 được khởi tạo với 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['']
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 Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
2

Chương trình tạo một

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 và một
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 và khẳng định rằng
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
87 của chúng đượ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ứ 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['']
25 cho đến nay

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
3

Chương trình thực thi chính xác, vì vậy có vẻ như

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 chỉ là trường hợp đặc biệt của
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86

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

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86, vì vậy bạn thực hiện các thay đổi thích hợp cho lớp

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
4

# 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['']
29 lấ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['']
30 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['']
31 cho đối tượng. Bạn có thể thêm đoạn 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 Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
5

Bạn thay đổi kích thước đối tượng hình chữ nhật và xác nhận 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 Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
3

Xác nhận 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ì sẽ xảy ra nếu bạn thay đổi kích thước một hình vuông?

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
7

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

# 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['']
33 mà bạn đã sử dụng với
# 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['']
34 và in vùng. Khi bạn chạy chương trình, bạn sẽ thấy

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
8

Chương trình cho thấy diện tích mớ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['']
35 giống như đố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['']
34. Vấn đề bây giờ là đố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['']
32 không còn đáp ứng ràng buộc của lớp
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 rằng
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
95 và
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
94 phải bằng nhau

Làm thế nào bạn có thể khắc phục vấn đề đó? . You can override

# 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['']
29 in
# 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['']
32 and ignore the
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
94 parameter, but that will be confusing for people looking at other parts of the program where
# 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['']
44 are being resized and some of them are not getting the expected areas because they are really
# 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['']
45

Trong một chương trình nhỏ như chương trình 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 ra 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 theo cả hai cách, thì bạn không nên lấy lớp này từ lớp khác

Trong ví dụ này, không có nghĩa là

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 kế thừa giao diện và triển khai của
# 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['']
29 từ
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86. Điều đó không có nghĩa là các đối tượng
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 không thể thay đổi kích thước. It means that the interface is different because it only needs a
# 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 parameter

This difference in interface justifies not deriving

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
88 from
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
86 like the test above advised

Remove ads

Mixing Features With Mixin Classes

One of the uses of multiple inheritance in Python is to extend a class features through mixins. A mixin is a class that provides methods to other classes but are not considered a base class

A mixin allows other classes to reuse its interface and implementation without becoming a super class. They implement a unique behavior that can be aggregated to other unrelated classes. They are similar to composition but they create a stronger relationship

Let’s say you want to convert objects of certain types in your application to a dictionary representation of the object. You could provide a

# 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['']
53 method in every class that you want to support this feature, but the implementation of
# 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['']
53 seems to be very similar

This could be a good candidate for a mixin. You start by slightly modifying 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__']
51 class from the composition example

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
9

The change is very small. You just changed the

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
45 and
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
59 attributes to be internal by adding a leading underscore to their name. You will see soon why you are making that change

Now, you add the

# 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['']
58 class

# 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

The

# 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['']
58 class exposes a
# 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['']
53 method that returns the representation of itself as a dictionary. Phương pháp này được triển khai dưới dạng một cách hiể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['']
61 có nội dung: “Tạo ánh xạ từ điể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['']
62 đế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['']
63 cho mỗi mục 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['']
64 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['']
62 không phải là nội bộ. ”

Note. This is why we made the role and payroll attributes internal in 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__']
51 class, because we don’t want to represent them in the dictionary

As you saw at the beginning, creating a class inherits some members 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__']
31, and one of those members is
>>> 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__']
29, which is basically a mapping of all the attributes in an object to their value

You iterate through all the items in

>>> 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__']
29 and filter out the ones that have a name that starts with an underscore using
# 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['']
70

# 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['']
71 checks the specified value. If the value is an
>>> 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__']
31, then it looks to see if it also has a
# 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['']
53 member and uses it to represent the object. Otherwise, it returns a string representation. If the value is not an
>>> 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__']
31, then it simply returns the value

You can modify 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__']
51 class to support this mixin

# 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

All you have to do is inherit the

# 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['']
58 to support the functionality. It will be nice to support the same functionality in the
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 class, so the
# 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['']
78 attribute is represented in the same way

# 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

You apply the mixin to the

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 class to support the feature. Now, you can write a small program to test it

# 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

The program implements a

# 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['']
80 that converts the dictionary to a JSON string using indentation so the output looks better

Then, it iterates through all the employees, printing the dictionary representation provided by

# 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['']
53. You can run the program to see its output

# 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

You leveraged the implementation of

# 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['']
58 in both
>>> 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__']
51 and
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 classes even when they are not related. Because
# 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['']
58 only provides behavior, it is easy to reuse with other classes without causing problems

Remove ads

Composition to Model “Has A” Relationship

Composition models a has a relationship. With composition, a class

>>> 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__']
02 has an instance of class
>>> 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__']
03 and can leverage its implementation. 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__']
03 class can be reused in other classes completely unrelated to 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__']
02

In the composition example above, 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__']
51 class has an
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 object.
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 implements all the functionality to handle addresses, and it can be reused by other classes

Other classes like

>>> 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__']
41 or
# 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['']
94 can reuse
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 without being related to
>>> 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__']
51. They can leverage the same implementation ensuring that addresses are handled consistently across the application

A problem you may run into when using composition is that some of your classes may start growing by using multiple components. Your classes may require multiple parameters in the constructor just to pass in the components they are made of. This can make your classes hard to use

A way to avoid the problem is by using the Factory Method to construct your objects. You did that with the composition example

If you look at the implementation of the

>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
41 class, you’ll notice that it uses
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
51 to construct an
>>> 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__']
51 object with the right parameters

This design will work, but ideally, you should be able to construct an

>>> 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__']
51 object just by specifying an
>>> 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__']
48, for example
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
02

The following changes might improve your design. You can start with the

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
34 module

# 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

First, you make the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
04 class internal, and then provide a
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
05 internal variable to the module. You are communicating to other developers that they should not create or use the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
04 directly. Instead, you provide two functions,
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
07 and
>>> 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, as the public interface to the module. This is what other modules should use

What you are saying is that the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
04 is a Singleton, and there should only be one object created from it

Now, you can do the same with the

>>> 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__']
21 module

# 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

Again, you make the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
11 internal and provide a public interface to it. The application will use the public interface to get policies and calculate payroll

You will now do the same with the

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
72 module

# 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

You are basically saying that there should only be one

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
13, one
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
11, and one
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
04. Again, this design pattern is called the Singleton design pattern, which comes in handy for classes from which there should only be one, single instance

Now, you can work on the

>>> 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__']
70 module. You will also make a Singleton out of the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
17, but you will make some additional changes

# 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

You first import the relevant functions and classes from other modules. The

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
17 is made internal, and at the bottom, you create a single instance. This instance is public and part of the interface because you will want to use it in the application

You changed the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
19 attribute to be a dictionary where the key is the employee
>>> 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__']
48 and the value is the employee information. You also exposed a
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
21 method to return the information for the specified employee
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
22

The

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
23 property now sorts the keys to return the employees sorted by their
>>> 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__']
48. You replaced the method that constructed 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__']
51 objects with calls to 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__']
51 initializer directly

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__']
51 hiện được khởi tạo vớ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__']
48 và sử dụng các hàm công khai có trong các mô-đun khác để khởi tạo các thuộc tính của nó

You can now change the program to test the changes

# 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
9

You import the relevant functions from the

>>> 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__']
21 and
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
34 modules, as well as the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
31 and
>>> 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__']
51 class. The program is cleaner because you exposed the required interface and encapsulated how objects are accessed

Notice that you can now create an

>>> 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__']
51 object directly just using its
>>> 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__']
48. You can run the program to see its output

# 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
0

The program works the same as before, but now you can see that a single

>>> 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__']
51 object can be created from its
>>> 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__']
48 and display its dictionary representation

Xem kỹ lớp họ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__']
51

# 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

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__']
51 class is a composite that contains multiple objects providing different functionality. It contains an
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 that implements all the functionality related to where the employee lives

>>> 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__']
51 also contains a productivity role provided by the
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
34 module, and a payroll policy provided by the
>>> 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__']
21 module. These two objects provide implementations that are leveraged by 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__']
51 class to track work in the
>>> 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__']
88 method and to calculate the payroll in 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__']
47 method

You are using composition in two different ways. The

>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
58 class provides additional data to
>>> 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__']
51 where the role and payroll objects provide additional behavior

Still, the relationship between

>>> 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__']
51 and those objects is loosely coupled, which provides some interesting capabilities that you’ll see in the next section

Composition to Change Run-Time Behavior

Inheritance, as opposed to composition, is a tightly couple relationship. With inheritance, there is only one way to change and customize behavior. Method overriding is the only way to customize the behavior of a base class. This creates rigid designs that are difficult to change

Composition, on the other hand, provides a loosely coupled relationship that enables flexible designs and can be used to change behavior at run-time

Imagine you need to support a long-term disability [LTD] policy when calculating payroll. The policy states that an employee on LTD should be paid 60% of their weekly salary assuming 40 hours of work

With an inheritance design, this can be a very difficult requirement to support. Adding it to the composition example is a lot easier. Let’s start by adding the policy class

# 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

Notice that

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
49 doesn’t inherit
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
06, but implements the same interface. This is because the implementation is completely different, so we don’t want to inherit any of the
>>> class MyError[Exception]:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
__main__.MyError
06 implementation

The

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
49 initializes
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
53 to
>>> class MyError:
..     pass
...
>>> raise MyError[]

Traceback [most recent call last]:
  File "", line 1, in 
TypeError: exceptions must derive from BaseException
68, and provides an internal
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
55 method that raises an exception if the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
56 has not been applied. Then, it provides a
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
57 method to assign the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
53

The public interface first checks that the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
53 has been applied, and then implements the functionality in terms of that base policy. The
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
60 method just delegates to the base policy, and
>>> 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__']
47 uses it to calculate the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
62 and then return the 60%

You can now make a small change to 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__']
51 class

# 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
3

You added an

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
64 method that applies the existing payroll policy to the new policy and then substitutes it. You can now modify the program to apply the policy to an
>>> 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__']
51 object

# 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
4

The program accesses

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
66, which is located at index
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
67, creates the
# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
49 object, and applies the policy to the employee. When
>>> 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__']
47 is called, the change is reflected. You can run the program to evaluate the output

# 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
5

The check amount for employee Kevin Bacon, who is the sales employee, is now for $1080 instead of $1800. That’s because the

# In hr.py

class Employee:
    def __init__[self, id, name]:
        self.id = id
        self.name = name
49 has been applied to the salary

As you can see, you were able to support the changes just by adding a new policy and modifying a couple interfaces. This is the kind of flexibility that policy design based on composition gives you

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

Python, as an object oriented programming language, supports both inheritance and composition. You saw that inheritance is best used to model an is a relationship, whereas composition models a has a relationship

Sometimes, it’s hard to see what the relationship between two classes should be, but you can follow these guidelines

  • Use inheritance over composition in Python 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 tính kế thừa trên thành phần trong Python để tận dụng cả giao diện và triển khai của lớp cơ sở

  • Use inheritance over composition in Python to provide mixin features to several unrelated classes when there is only one implementation of that feature

  • Sử dụng thành phần thay vì thừa kế trong Python để mô hình hóa mối quan hệ thúc đẩy việc triển khai lớp thành phần

  • Use composition over inheritance in Python to create components that can be reused by multiple classes in your Python applications

  • Use composition over inheritance in Python to implement groups of behaviors and policies that can be applied interchangeably to other classes to customize their behavior

  • Use composition over inheritance in Python to enable run-time behavior changes without affecting existing classes

Conclusion

You explored 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

In this article, you learned how to

  • Use inheritance to express an is a relationship between two classes
  • Đánh giá nếu thừa kế là mối quan hệ đúng
  • Sử dụng đa kế thừa trong Python và đánh giá MRO của Python để khắc phục sự cố đa kế thừa
  • Mở rộng các lớp với mixin và sử dụng lại việc triển khai chúng
  • Sử dụng thành phần để thể hiện một mối quan hệ giữa hai lớp
  • Cung cấp các thiết kế linh hoạt bằng cách sử dụng thành phần
  • Reuse existing code through policy design based on composition

Recommended Reading

Here are some books and articles that further explore object oriented design and can be useful to help you understand the correct use of inheritance and composition in Python or other languages

  • Design Patterns. Elements of Reusable Object-Oriented Software
  • Head First Design Patterns. A Brain-Friendly Guide
  • Clean Code. A Handbook of Agile Software Craftsmanship
  • SOLID Principles
  • Liskov Substitution Principle

Mark as Completed

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. Hướng dẫn OOP Python

🐍 Python Tricks 💌

Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. No spam ever. Unsubscribe any time. Curated by the Real Python team

Send Me Python Tricks »

About Isaac Rodriguez

Hi, I'm Isaac. I build, lead, and mentor software development teams, and for the past few years I've been focusing on cloud services and back-end applications using Python among other languages. Love to hear from you here at Real Python

» More about Isaac

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are

Alex

Aldren

Joanna

Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng Pythonistas chuyên gia

Level Up Your Python Skills »

What Do You Think?

Đánh giá bài viết này

Tweet Chia sẻ Chia sẻ Email

Bài học số 1 hoặc điều yêu thích mà bạn đã học được là gì?

Mẹo bình luận. The most useful comments are those written with the goal of learning from or helping out other students. and get answers to common questions in our support portal

What is multilevel inheritance in Python?

Kế thừa đa cấp trong Python là một loại Kế thừa liên quan đến việc kế thừa một lớp đã kế thừa một số lớp khác . That means the derived/subclass class inherits the features of the base class/parent class, and the new derived class inherits the features of the derived class.

Is multilevel inheritance allowed in Python?

Có, Python hỗ trợ đa kế thừa . Giống như C++, một lớp có thể được bắt nguồn từ nhiều lớp cơ sở trong Python. Điều này được gọi là Đa kế thừa.

Kế thừa thừa kế đa cấp là gì?

Kế thừa đa cấp trong C++ là quá trình dẫn xuất một lớp từ một lớp dẫn xuất khác . When one class inherits another class it is further inherited by another class. Nó được gọi là kế thừa đa cấp.

How many levels of inheritance are allowed in Python?

Depending upon the number of child and parent classes involved, there are four types of inheritance in python.

Chủ Đề