Giới thiệu chung về class trong Python, constructor

    5

    Bắt đầu từ bài học này chúng ta sẽ chuyển sang lập trình hướng đối tượng với Python với trọng tâm là kỹ thuật xây dựng class.

    Python là một ngôn ngữ lập trình hướng đối tượng. Mọi phần tử trong chương trình Python thực chất đều là các object từ các class đã xây dựng sẵn: int, float, str, list, function. Python cũng cung cấp khả năng xây dựng các class mới. Nhìn chung các kỹ thuật xây dựng class trong Python tương đối đơn giản hơn so với các ngôn ngữ như C++, C#, Java.

    Như đã trình bày ngay từ đầu, đây không phải là một khóa học nhập môn lập trình với Python, cũng không phải là khóa học lập trình hướng đối tượng. Chúng tôi giả định rằng bạn đã nắm được những khái niệm cơ bản của lập trình hướng đối tượng (class, object, kế thừa, đóng gói, đa hình, v.v.). Do vậy, các bài học sẽ không trình bày chi tiết các khái niệm cơ bản mà sẽ đi vào đặc trưng sử dụng chúng trong Python.

    Khai báo class trong Python

    Python là một ngôn ngữ lập trình hướng đối tượng hoàn toàn, nghĩa là mọi phần tử trong chương trình Python đều là object. Một số, một chuỗi, một danh sách, v.v., mà bạn đã biết thực ra đều là object của một class xây dựng sẵn (int, float, str, list, v.v.). Một hàm (xây dựng sẵn hoặc do bạn tự xây dựng bằng từ khóa def) cũng là một object.

    Như vậy, khi làm việc với Python từ đầu khóa học đến giờ, trên thực tế là bạn đã trực tiếp sử dụng class và object. Định nghĩa class và object trong Python hoàn toàn tương tự như trong các ngôn ngữ lập trình khác.

    Python cho phép người lập trình tự xây dựng class riêng của mình.

    Hãy cùng bắt đầu với một ví dụ.

    Tạo module book.py và viết code như sau:

    class Book:
        """A class for e-book"""
        
        def __init__(self, 
                     title: str,
                     authors: str = '',
                     publisher: str = '',
                     year: int = 2020,
                     edition: int = 1):
            """Hàm tạo của class"""
            self.title = title
            self.authors = authors
            self.publisher = publisher
            self.year = year
            self.edition = edition
            
        def print(self):
            """Print the book infor"""
            print(f"{self.title} by {self.authors}, {self.edition} edition, {self.publisher}, {self.year}")

    Đây là code tạo một class mô tả sách, bao gồm các thông tin về tựa sách, tác giả, nhà xuất bản, năm xuất bản, lần tái bản.

    Class trong Python được khai báo với từ khóa class theo cấu trúc như sau:

    class tên_class:
        '''docstring'''
        # class suite

    Lệnh khai báo class cũng là một lệnh phức hợp với 1 clause. Header bao gồm từ khóa class và tên class, phần suite chứa các lệnh khai báo các thành phần của class.

    Tên class phải tuân thủ theo quy tắc đặt định danh chung của Python. Ngoài ra, tên class được đặt theo quy ước PascalCase (viết hoa chữ cái đầu mỗi từ). Để ý rằng, các class xây dựng sẵn của Python lại chỉ đặt tên viết thường.

    Ngay dưới header là docstring của class. Docstring cung cấp tài liệu hỗ trợ cho việc sử dụng class, tương tự như vai trò của docstring trong hàm. Thông thường docstring của class đơn giản hơn vì chỉ tóm lược mục đích sử dụng của class.

    Sau phần docstring là khai báo các thành phần khác của class. Class trong Python có nhiều thành phần khác nhau. Phần tiếp theo của bài học sẽ giới thiệu sơ lược về các thành phần này. Trong các bài học sau chúng ta sẽ lần lượt học cách làm việc với chúng.

    Sử dụng class

    Với class Book xây dựng như trên, bạn có thể tạo object như sau:

    from book import Book
    b1 = Book('Lập trình hướng đối tượng với Python', 'Nhật Linh', 'Tự học ICT', 2022, 2)
    b1.print()
    b2 = Book(title = 'Nhập môn lập trình Python', authors= 'Nhật linh', publisher= 'Tự học ICT')
    b2.print()
    b3 = Book('A new book')
    b3.print()

    Dễ thấy rằng, lệnh tạo object không khác biệt gì so với lời gọi hàm thông thường.

    Như vậy, lệnh tạo object đơn giản nhất là sử dụng tên class và cặp dấu (), tương tự như một lời gọi hàm.

    Phụ thuộc vào hàm tạo của class, lời gọi lệnh tạo object có thể phức tạp hơn với nhiều tham số, giống như lời gọi hàm thông thường. Hàm tạo sẽ được trình bày chi tiết ở phần sau của bài học này.

    Bạn có thể xây dựng và sử dụng class trong cùng một module. Tuy nhiên, thông thường class nên được xây dựng trong module riêng.

    Nếu class xây dựng trong một module/package khác, bạn cần import nó trước khi sử dụng bằng một trong hai cách đã học.

    Ví dụ, nếu class Book xây dựng trong module book, bạn có thể sử dụng from book import Book hoặc import book.

    from book import Book
    #hoặc
    import book

    Nếu sử dụng from book import Book bạn có thể sử dụng trực tiếp tên class Book.

    Khi sử dụng import book, bạn phải sử dụng thêm tên module: b4 = book.Book('Lập trình Python')

    Các thành phần chính trong class Python

    Mặc dù bạn có thể khai báo một class hoàn toàn không chứa thành viên nào, class như vậy không có giá trị. Class trong Python thường chứa những thành phần sau:

    • Constructor (hàm tạo)
    • Các attribute (biến)
    • Các method (phương thức)
    • Các property (thuộc tính)

    Hàm tạo (constructor) là hàm được gọi trong quá trình tạo object của class. Khác với C# hay Java, hàm tạo trong Python không phải là hàm chạy đầu tiên khi tạo object, và cũng không phải là hàm chịu trách nhiệm tạo object. Hàm tạo trong Python có tác dụng tạo các đặc tính thành viên (instance attribute). Chúng ta sẽ học cách làm việc với hàm tạo ở phần sau của bài học này.

    Attribute (biến/ đặc tính) là thành phần chứa dữ liệu trong class/object. Python phân biệt hai loại attribute: instance attribute và class attribute.

    Instance attribute là những biến chứa trạng thái của một object cụ thể và đặc trưng cho object. Trong Python, instance attribute được khai báo và gán giá trị trong hàm tạo.

    Class attribute là những biến chứa giá trị đặc trưng cho cả class chứ không đặc trưng cho một object cụ thể. Class attribute có cùng giá trị trong tất cả các object của class. Class attribute có thể được sử dụng, ví dụ, để theo dõi số lượng object của class được tạo ra.

    Phương thức (method) là thành phần xử lý dữ liệu trong Python. Phương thức thực chất là các loại hàm khác nhau được khai báo trong class. Python phân biệt instance method, class method và static method.

    Instance method là những hàm xử lý trạng thái của object. Instance method gắn liền với object và sử dụng các instance attribute (dữ liệu gắn với từng object).

    Class method là những hàm xử lý thông tin của class và gắn liền với class. Class method chuyên xử lý class attribute.

    Static method là loại phương thức đặc biệt không sử dụng bất kỳ thông tin gì của class hay object mặc dù nằm trong class.

    Các thành phần của class trong Python có nhiều điểm tương tự với các ngôn ngữ như C# hay Java. Tuy nhiên, tên gọi trong Python có phần hơi khác. Instance attribute tương tự như biến thành viên trong C# hay Java. Class attribute tương tự như biến thành viên tĩnh trong C#. Class method và static method tương tự như static method trong C#.
    Cách gọi tên trong Python rất hệ thống, phân biệt rõ ràng đặc trưng của instance và đặc trưng của class.

    Trong bài học này và một số bài học kế tiếp chúng ta sẽ xem xét ý nghĩa và cách xây dựng các thành viên của class Python.

    Constructor trong Python

    Hãy xem lại hàm __init__() mà chúng ta đã xây dựng trong ví dụ đầu tiên:

    def __init__(self, 
                    title: str,
                    authors: str = '',
                    publisher: str = '',
                    year: int = 2020,
                    edition: int = 1):
        """Hàm tạo của class"""
        self.title = title
        self.authors = authors
        self.publisher = publisher
        self.year = year
        self.edition = edition
        self.__private = True
        Book.count += 1

    __init__() là một hàm đặc biệt trong Python: hàm tạo (constructor).

    Về mặt hình thức __init__() hoàn toàn tương tự như một lệnh khai báo hàm trong Python. Hàm __init__() ở trên nhận các tham số title, authors, publisher, year và edition. Chúng ta cũng sử dụng kỹ thuật chỉ báo kiểu (type hint) và cung cấp giá trị mặc định cho các tham số.

    Constructor trong Python bắt buộc phải có tên là __init__ và phải có ít nhất một tham số, thường đặt tên là self. Nếu có nhiều tham số, self bắt buộc phải là tham số đầu tiên.

    Tên gọi tham số self được đặt theo quy ước của Python chứ không bắt buộc. Bạn có thể đặt bất kỳ tên gọi nào khác. Những bạn có xuất phải điểm là C++ hay C# thường có xu hướng đặt là this.

    Constructor và Initializer

    Nói một cách chính xác, __init__() không phải là constructor theo nghĩa đen của khái niệm này trong lập trình hướng đối tượng. Hàm __init__() là một initializer – hàm chịu trách nhiệm khởi tạo các giá trị cho object. Initializer không chịu trách nhiệm khởi tạo object.

    Trong Python, hàm __init__() không chịu trách nhiệm tạo ra object của class. Python sử dụng một ‘magic method’ có tên gọi là __new__() để tạo object của mỗi class.

    Magic method là một số phương thức được Python tự động tạo cùng với class và được Python gọi tự động nhằm thực hiện những công việc đặc biệt.

    __new__() mới thực sự là constructor của Python class.

    Ví dụ, khi gặp lệnh tạo object b = book() thì Python sẽ tự động chạy hàm __new__() đầu tiên. Kết quả của hàm __new__() là object của class book. Sau đó Python tiếp tục tự động chạy __init__(). Object do __new__() tạo ra được truyền sang cho __init__() thông qua tham số đầu tiên (self) trong danh sách.

    Vì lý do này, bạn có thể đặt bất kỳ tên gì cho self cũng được nhưng phải để nó ở đầu danh sách tham số.

    Vai trò quan trọng hàng đầu của hàm tạo trong Python là tạo và gán giá trị cho instance attribute. Tất cả các tham số còn lại trong danh sách tham số của __init__() cung cấp giá trị để tạo ra instance attribute cho object.

    Với hàm tạo như trên, bạn có thể tạo object của class Book bằng những cách sau:

    b1 = Book('Lập trình hướng đối tượng với Python', 'Nhật Linh', 'Tự học ICT', 2022, 2)
    b2 = Book(title = 'Nhập môn lập trình Python', authors= 'Nhật linh', publisher= 'Tự học ICT')
    b3 = Book('A new book')

    Dễ thấy rằng, lệnh tạo object bằng hàm tạo không khác biệt gì so với lời gọi hàm thông thường.

    Đặc điểm của attribute và method trong class Python

    Attribute và method trong Python có điểm khác biệt với biến thành viên và phương thức trong các ngôn ngữ C++/C#/Java.

    Hãy xem ví dụ sau:

    class Book:
        """A class for e-book"""
    b = Book()
    b.title = 'Python programming'
    b.authors = 'Donald Trump'
    b.year = 2020
    print(b.title, b.authors, b.year) # kết quả là 'Python programming Donald Trumo 2020'
    b2 = Book()
    print(b2.title) # lỗi, không có attribute title trong object b2

    Bạn có thể thấy rất nhiều điều lạ ở đây. Dễ thấy nhất là class Book hoàn toàn trống trơn. Trong class này chỉ có mỗi docstring. Tuy nhiên sau khi tạo object b, bạn lại có thể dùng phép toán truy xuất phần tử (dot notation) b.title, b.authors, b.year, gán giá trị cho chúng và sau sử dụng lại chúng trong hàm print(). Rõ ràng bạn không hề tạo title, authors hay year trong khai báo Book.

    Khi bạn tạo object b2 và thử truy xuất giá trị title (b2.title) thì lại gặp lỗi “không tìm thấy attribute title trong object b2”.

    title, authors, year được gọi là những attribute của object b (nhưng không phải là attribute của b2).

    Như vậy, trong Python, attribute là những biến có thể chứa giá trị đặc trưng cho một object. Nó được tạo hoàn toàn độc lập với khai báo class (không cần chỉ định trong khai báo class). Một cách chính xác hơn, biến này được gọi là instance attribute (do liên quan đến object).

    Giờ hãy xem một ví dụ khác:

    class Person:    
        pass
    putin = Person()
    def greeting(msg:str):
        print(msg)
         
    putin.say_hello = greeting
    putin.say_hello('Hello world from Python method') # in ra dòng 'Hello world from Python method'
    trump = Person()
    trump.say_hello('Welcome to heaven!') # lệnh này sẽ bị lỗi 'không tìm thấy hàm say_hello
    

    Trong ví dụ này:

    1. Chúng ta khai báo một class trống rỗng Person. Trong class này không có bất kỳ thành viên nào.
    2. Tiếp theo chúng ta tạo object putin của class Person.
    3. Chúng ta khai báo một hàm độc lập greeting() có thể in ra dòng thông báo.
    4. Điểm rất đặc biệt là lệnh putin.say_hello = greeting. Đây là lệnh tạo ra một phương thức (method) trong object putin và gán cho nó hàm greeting(). Tức là chúng ta ‘gán ghép’ greeting() với say_hello() của putin.
    5. Sau đó, bạn có thể sử dụng hàm/phương thức greeting từ object putin nhưng với tên gọi mới say_hello. Hàm say_hello() được gọi là một method của putin.
    6. Tuy nhiên, nếu bạn tạo object trump từ class Person, object này lại không có phương thức say_hello.

    Như vậy, giống như attribute, method trong class Python cũng không bắt buộc phải khai báo trong thân class. Method độc lập với class và object.

    Kết luận

    Trong bài học này chúng ta bắt đầu với lập trình hướng đối tượng trong Python:

    • Nhìn chung trong Python vẫn sử dụng các khái niệm cơ bản tương tự như trong các ngôn ngữ lập trình hướng đối tượng khác, mặc dù tên gọi có chút khác biệt.
    • Python phân biệt thành phần dữ liệu (attribute) và thành phần xử lý (method) trong class. Đồng thời Python cũng phân biệt rõ các thành viên liên quan đến class và thành viên liên quan đến object. Từ đây dẫn đến sự phân biệt instance attribute/class attribute, instance method/class method. Chi tiết về attribute method sẽ được trình bày trong hai bài học tương ứng.
    • Python có điểm đặc biệt trong khởi tạo object và cách xây dựng hàm tạo. Nó có thể gây khó khăn cho những bạn xuất phát từ C++/Java/C#.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    Theo dõi
    Thông báo của
    guest

    5 Thảo luận
    Cũ nhất
    Mới nhất
    Phản hồi nội tuyến
    Xem tất cả bình luận
    Jerrydinh

    Xin cho hỏi:
    trong ví dụ bài học này, phần khai báo tham số cho hàm __init__()
    Cấu trúc khai báo: author:str = ” có nghĩa là gì?
    Xin cám ơn!

    Jerrydinh

    xin cho hỏi rõ thêm: câu lệnh author:str = ‘ ‘ có phải là kiểu khai báo tham số mặc định trong hàm, trong đó tham số có kiểu dữ liệu là str và giá trị mặc định của nó là 1 chuỗi rỗng? và như bạn đã giải thích ở trên, str chỉ là 1 thông báo, không bắt buộc và có thể thay đổi khi truyền tham số nếu muốn???

    Phuong Do

    Hay quá ạ. Phần nói về biến và phương thức của class rất rõ ràng dễ hiểu. Em tìm trên mạng không thấy bài viết nào trình bày chi tiết dễ hiểu như thế này. Cảm ơn các anh chị!