Kế thừa class trong Python (Phần 2)

654
18-10-2021
Kế thừa class trong Python (Phần 2)

Trong phần trước, mình đã đề cập các tính chất cơ bản của class trong Python. Trong số này, mình sẽ đề cập về cách tính chất, đặc điểm nâng cao của class.

I. Kế thừa nhiều classes (Multi-inheritance)

Không như Java — ngôn ngữ chỉ cho kế thừa một class duy nhất cho một class, Python cho phép cho chúng ta kết thừa nhiều class cho một class. Ví dụ:

class Father:
    pass

class Mother:
    pass

class Child(Father, Mother):
    pass

Kết quả của Child.bases trong trường hợp này đó là:

(__main__.Father, __main__.Mother)

II. Tìm attributes với kế thừa

Theo logic, quá trình tìm kiếm attributes như sau:

  • Đầu tiên, tìm kiếm attributes trong local dict
  • Nếu không có, tiếp tục tìm trong class dict
  • Nếu vẫn không có thì tìm ở trong các classes ở mro

Mình sẽ làm rõ ý cuối cùng ở các phần tiếp theo của bài.

III. MRO

MRO là tên viết tắt của Method Resolution Order. Là một chuỗi inheritance mà Python tính toán và lưu nó ở MRO attribute trong class. Như đã nói ở trên khi tìm attributes, Python sẽ đi lần lượt qua các phần tử trong MRO.

Để xem MRO của một class, ta dùng cú pháp "<class>.__mro__". Ví dụ:

class A: pass
class B(A): pass
class C(B): pass
class D(C): pass
class E(D): pass
E.__mro__

Output:

(__main__.E, __main__.D, __main__.C, __main__.B, __main__.A, object)

Python sử dụng cooperative multiple inheritance để quy định một số luật về thứ tự của class:

  • Children are always checked before parents (children classes luôn được kiểm tra trước parents classes) Parents (if multiple) are always checked in the order listed (các parents classes luôn được kiêm tra theo thứ tự được liệt kê)
  • Như ví dụ ở trên, sau khi kiểm tra ở E, class D sẽ được kiểm tra vì D được ghi đầu tiên trong hai base classes. Sau đó, B — parent class của D sẽ được kiểm tra. Cuối cùng, class C và parent của nó - A sẽ được kiểm tra.

Để hiểu rõ hơn, các bạn hãy thử động não, thử giải thích MRO của class E dưới đây:

class A: pass
class B: pass
class C(A, B): pass
class D(B): pass
class E(C, D): pass
E.__mro__
(__main__.E, __main__.C, __main__.A, __main__.D, __main__.B, object)

Thuật toán này là có tên là "C3 Linearization Algorithm" nhưng để đơn giản và nhớ hiểu, hãy tưởng tượng đến thứ tự thoát hiểm khi một sự cố như cháy nhà, chìm tàu xảy ra: "Trẻ em đầu tiên, sau đó mới là người lớn" (Children first, followed by parents).

IV. Ví dụ về multi-inheritance

class Person:
    def talks(self):
        return 'alo alo'


class Noisy:
    def talks(self):
        return super().talk().upper()


class NoisyPerson(Noisy, Person):
    pass

super() sẽ gọi class kế tiếp của class hiện tại trong MRO

Ví dụ:

MRO của NoisyPerson:

(__main__.NoisyPerson, __main__.Noisy, __main__.Person, object)

Thử gọi method talks của NoisyPerson instance:

girl = NoisyPerson() girl.talks()

Output:

alo alo

Khi talk() method của girl được gọi, do bản thân NoisyPerson không có method này nên nó sẽ tìm ở class tiếp theo trong MRO là Noisy. Noisy có talk() method sẽ được execute. super().talk() sẽ tìm gọi method talk() của class kế tiếp trong MRO - Person (return 'alo alo'). Đoạn string 'alo alo' này sẽ được upper() và return. Do đó output trả về là "ALO ALO"

Bây giờ nếu ta đổi chỗ 2 parent classes với nhau thì sao?

class NoisyPerson(Person, Noisy):
    pass

Chạy lại talks method

girl = NoisyPerson()
girl.talks()

Output:

ALO ALO

Đây là MRO của NoisyPerson bây giờ:

(__main__.NoisyPerson, __main__.Person, __main__.Noisy, object)

Ta thấy class Person được kiểm tra trước Noisy, trong khi đó Person có method talks() nên nó sẽ được execute.

V. Kết luận

Qua hai phần về chủ đề class, hy vọng các bạn có thêm kiến thức về class và có thể áp dụng nó vào trong công việc của mình. Các bạn hãy đón chờ các bài tiếp theo về chủ đề Python trên tech blog Bizfly Cloud nha!

SHARE