Hướng dẫn super().__init__() trong python

Trong Python, hàm super() được sử dụng chính trong hai trường hợp:

  • Giúp chúng ta tránh phải sử dụng tên class cơ sở một cách rõ ràng
  • Xử lý Đa kế thừa

Ví dụ 1: Hàm super() với Kế thừa đơn

Trong trường hợp kế thừa đơn, hàm super() giúp chúng ta tham chiều tới class cơ sở.

Ví dụ:

class Mammal(object):
  def __init__(self, mammalName):
    print(mammalName, 'là động vật máu nóng.')
    
class Voi(Mammal):
  def __init__(self):
    print('Voi có 4 chân.')
    super().__init__('Voi')
    
d1 = Voi()

Khi chạy chương trình, kết quả thu được là

Voi có 4 chân.
Voi là động vật máu nóng.

Ở đây, chúng ta gọi method __init__() của lớp Mammal (từ class Voi) bằng đoạn mã:

super().__init__('Voi')

thay vì sử dụng:

Mammal.__init__(self, 'Voi')

Vì chúng ta không cần sử dụng tên của class cơ sở khi gọi các đối tượng bên trong nó nên khi cần chúng ta có thể đổi tên class một cách dễ dàng.

Ví dụ đổi tên class Mammal:

# đổi tên class Mammal thành Elephantidae
class Voi(Elephantidae):
  def __init__(self):
    print('Voi có 4 chân.')

    # chúng ta không cần thay đổi dòng code này
    super().__init__('Voi')
Hướng dẫn super().__init__() trong python
Hàm super() trong Python trả về một đối tượng proxy (đối tượng tạm thời của một siêu class) cho phép chúng ta truy cập các method của class cơ sở.

Hàm super() trả về một đối tượng proxy, một dạng đối tượng thay thế có thể gọi các method của class cơ sở thông qua ủy quyền. Đây được gọi là phương thức gián tiếp (khả năng tham chiếu một đối tượng cơ sở với hàm super()).

Vì phương thức tham chiếu gián tiếp này chỉ được sử dụng trong thời gian chạy chương trình nên chúng ta có thể sử dụng các class cơ sở khác nhau vào các thời điểm khác nhau nếu cần.

Ví dụ 2: Hàm super() với Đa kế thừa

Chúng ta hãy cùng xem đoạn code sau:

class Animal:
  def __init__(self, Animal):
    print(Animal, 'là một loài động vật.');

class Mammal(Animal):
  def __init__(self, mammalName):
    print(mammalName, 'là động vật máu nóng.')
    super().__init__(mammalName)
    
class NonWingedMammal(Mammal):
  def __init__(self, NonWingedMammal):
    print(NonWingedMammal, "không thể bay.")
    super().__init__(NonWingedMammal)

class NonMarineMammal(Mammal):
  def __init__(self, NonMarineMammal):
    print(NonMarineMammal, "không thể bơi.")
    super().__init__(NonMarineMammal)

class Voi(NonMarineMammal, NonWingedMammal):
  def __init__(self):
    print('Voi có 4 chân.');
    super().__init__('Voi')
    
d = Voi()
print('')
bat = NonMarineMammal('Dơi')

Khi chạy chương trình, kết quả thu được là

Voi có 4 chân.
Voi không thể bơi.
Voi không thể bay.
Voi là động vật máu nóng.
Voi là một loài động vật.

Dơi không thể bơi.
Dơi là động vật máu nóng.
Dơi là một loài động vật.

Phương pháp giải quyết thứ tự (Method Resolution Order - MRO)

MRO là thứ tự các method được kế thừa khi có đa kế thừa. Bạn có thể xem MRO bằng cách sử dụng thuộc tính __mro__.

>>> Voi.__mro__
(<class 'Voi'>, 
<class 'NonMarineMammal'>, 
<class 'NonWingedMammal'>, 
<class 'Mammal'>, 
<class 'Animal'>, 
<class 'object'>)

Đây là cách hoạt động của MRO

  • Một method trong các lệnh dẫn xuất luôn được gọi trước method của class cơ sở. Trong ví dụ của chúng ta, class Voi được gọi trước NonMarineMammal hoặc NonWingedMammal. Hai class này lại được gọi trước class MammalMammal lại được gọi trước Animal, cuối cùng mới đến đối tượng.
  • Nếu có nhiều class cha mẹ như Voi(NonMarineMammal, NonWingedMammal), các method của NonMarineMammal sẽ được gọi trước tiên vì nó xuất hiện đầu tiên.

Để tìm hiểu thêm về các hàm trong Python, mời các bạn truy cập trang web dưới đây:

  • Các hàm Python tích hợp sẵn

Cho đến nay, những chủ đề về lập trình hướng đối tượng trong Python mà chúng ta đã thảo luận là:

–  Phần 1 (lớp, đối tượng và các thành viên của lớp)

– Phần 2 (che giấu dữ liệu và in đối tượng)

Trong bài học này, tính kế thừa sẽ được giới thiệu.

Một trong các lợi thế chính của lập trình hướng đối tượng đó là khả năng tái sử dụng của code. Và kế thừa chính là một trong các cơ chế để đạt được điều đó. Trong kế thừa, một lớp (thường được gọi là lớp cha – super class) sẽ được kế thừa lại bởi một lớp khác (thường gọi là lớp con – subclass). Lớp con này sẽ bổ sung thêm một số thuộc tính cho lớp cha. 

Dưới đây là một đoạn chương trình được viết bằng Python, mô tả cách cài đặt kế thừa trong Python

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# A Python program to demonstrate inheritance  
  
# Base or Super class. Note object in bracket. 
# (Generally, object is made ancestor of all classes) 
# In Python 3.x "class Person" is  
# equivalent to "class Person(object)" 
class Person(object): 
      
    # Constructor 
    def __init__(self, name): 
        self.name = name 
  
    # To get name 
    def getName(self): 
        return self.name 
  
    # To check if this person is employee 
    def isEmployee(self): 
        return False
  
  
# Inherited or Sub class (Note Person in bracket) 
class Employee(Person): 
  
    # Here we return true 
    def isEmployee(self): 
        return True
  
# Driver code 
emp = Person("Geek1")  # An Object of Person 
print(emp.getName(), emp.isEmployee()) 
  
emp = Employee("Geek2") # An Object of Employee 
print(emp.getName(), emp.isEmployee()) 

Kết quả in ra là:

('Geek1', False)
('Geek2', True)

  • 1. Làm thế nào để kiểm tra xem một lớp có phải là lớp con của lớp khác không
  • 2. Object class – lớp đối tượng là gì?
  • 3. Python có hỗ trợ đã kế thừa – multiple inheritance không?
  • 4. Làm thế nào để truy cập tới các thành viên của lớp cha, tại lớp con?

1. Làm thế nào để kiểm tra xem một lớp có phải là lớp con của lớp khác không

Python có cung cấp một hàm có tên là issubclass(), có thể trực tiếp nói cho lập trình viên biết rằng lớp được xét này có phải là lớp con của một lớp khác hay không.


# Python example to check if a class is 
# subclass of another 
  
class Base(object): 
    pass   # Empty Class 
  
class Derived(Base): 
    pass   # Empty Class 
  
# Driver Code 
print(issubclass(Derived, Base)) 
print(issubclass(Base, Derived)) 
  
d = Derived() 
b = Base() 
  
# b is not an instance of Derived 
print(isinstance(b, Derived)) 
  
# But d is an instance of Base 
print(isinstance(d, Base)) 

Kết quả in ra là:

True
False
False
True

2. Object class – lớp đối tượng là gì?

Cũng giống như lớp đối tượng trong Java, bắt đầu từ phiên bản 3.x của Python, đối tượng là gốc rễ của tất cả các lớp.

Trong Python 3.x, “class Test(object)” và “class Test” là như nhau.

Trong Python 2.x, “class Test(object)” sẽ tạo ra một lớp có đối tượng object làm lớp cha (được gọi là new style class), còn “class Test” sẽ tạo ra một old style class – lớp bình thường (không có đối tượng nào làm lớp cha của nó)

3. Python có hỗ trợ đã kế thừa – multiple inheritance không?

Không giống như Java và giống như C++, Python có hỗ trợ đã kế thừa. Chúng ta có thể chỉ định nhiều lớp cha, phân cách nhau bởi dấu phẩy, và tất cả được bao trong một cặp dấu ngoặc tròn () để thực hiện đa kế thừa. 

Dưới đây là ví dụ mô tả cách thức hoạt động của đa kế thừa trong Python


# Python example to show working of multiple  
# inheritance 
class Base1(object): 
    def __init__(self): 
        self.str1 = "Cafedev1"
        print "Base1"
  
class Base2(object): 
    def __init__(self): 
        self.str2 = "Cafedev2"        
        print "Base2"
  
class Derived(Base1, Base2): 
    def __init__(self): 
          
        # Calling constructors of Base1 
        # and Base2 classes 
        Base1.__init__(self) 
        Base2.__init__(self) 
        print "Derived"
          
    def printStrs(self): 
        print(self.str1, self.str2) 
         
  
ob = Derived() 
ob.printStrs() 

Kết quả in ra là:

Cafedev1
Cafedev2
Derived
('Cafedev1', 'Cafedev2')

4. Làm thế nào để truy cập tới các thành viên của lớp cha, tại lớp con?

1. Sử dụng tên lớp cha

Đoạn code ví dụ cho thấy các thành viên của lớp cha có thể được truy cập tới từ bên trong các lớp con, bằng cách sử dụng tên của lớp cha.


# Python example to show that base 
# class members can be accessed in 
# derived class using base class name 
class Base(object): 
  
    # Constructor 
    def __init__(self, x): 
        self.x = x     
  
class Derived(Base): 
  
    # Constructor 
    def __init__(self, x, y): 
        Base.x = x  
        self.y = y 
  
    def printXY(self): 
       
       # print(self.x, self.y) will also work 
       print(Base.x, self.y) 
  
  
# Driver Code 
d = Derived(10, 20) 
d.printXY() 

Kết quả in ra là:

(10, 20)

2. Sử dụng hàm super()

Chúng ta cũng có thể truy cập tới các thành viên của lớp cha bằng cách sử dụng hàm super().

Đoạn code dưới đây sẽ mô tả cách sử dụng hàm super() để truy cập tới các thành viên của lớp cha, từ lớp con

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python example to show that base 
# class members can be accessed in 
# derived class using super() 
class Base(object): 
  
    # Constructor 
    def __init__(self, x): 
        self.x = x     
  
class Derived(Base): 
  
    # Constructor 
    def __init__(self, x, y): 
          
        ''' In Python 3.x, "super().__init__(name)" 
            also works''' 
        super(Derived, self).__init__(x) 
        self.y = y 
  
    def printXY(self): 
  
       # Note that Base.x won't work here 
       # because super() is used in constructor 
       print(self.x, self.y) 
  
  
# Driver Code 
d = Derived(10, 20) 
d.printXY() 

Kết quả in ra là:

(10, 20)

Lưu ý rằng, hai phương thức ở trên không hoàn toàn giống nhau. Trong bài học tiếp theo về kế thừa, chúng ta sẽ thảo luận tiếp hai chủ đề sau:

– Cách thức hoạt động của super? Làm thế nào mà việc truy cập tới thành viên (biến thành viên, hàm thành viên) của lớp cha, thông qua super và tên lớp cha lại khác nhau?

– Vấn đề Diamon (kim cương) được giải quyết thế nào trong Python?

Nguồn và Tài liệu tiếng anh tham khảo:

  • w3school
  • python.org
  • geeksforgeeks

Tài liệu từ cafedev:

  • Full series tự học Python từ cơ bản tới nâng cao tại đây nha.
  • Ebook về python tại đây.
  • Các series tự học lập trình khác

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

  • Group Facebook
  • Fanpage
  • Youtube
  • Instagram
  • Twitter
  • Linkedin
  • Pinterest
  • Trang chủ

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

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!