Python cơ bản
Python hướng đối tượng
Python nâng cao
Quảng cáo

Class và hàm constructor

Bài viết này giúp các bạn hiểu rõ về class trong lập trình hướng đối tượng bằng ngôn ngữ Python cùng với Hàm khởi tạo constructor trong lập trình Python

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 cách 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ư ngay từ ban đầu các bạn đã được thông báo rằng các bài hướng dẫn này không phải là dành cho những người mới học về lập trình, do đó trong bài nay có một số khái niệm cơ bản về lập trình hướng đối tượng (class, object, kế thừa ...) sẽ không được trình bày chi tiết mà chỉ sẽ đi nghiên cứu cách sử dụng chúng trong lập trình Python

Cách 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:

Ví dụ

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:

Ví dụ

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.

Cách sử dụng class trong Python

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

Ví dụ

from book import Book

b1 = Book('Lập trình hướng đối tượng với Python', 'Hoàng Nam', 'VZN.VN', 2022, 2)
b1.print()

b2 = Book(title = 'Nhập môn lập trình Python', authors= 'Hoàng Nam', publisher= 'VZN.VN')
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.

Ví dụ

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 của một class trong lập trình 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.

Hàm 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:

Ví dụ

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 khởi 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:

Ví dụ

b1 = Book('Lập trình hướng đối tượng với Python', 'Hoàng Nam', 'VZN.VN', 2022, 2)
b2 = Book(title = 'Nhập môn lập trình Python', authors= 'Hoàng Nam', publisher= 'VZN.VN')
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 của class trong 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:

Ví dụ

book.pyperson.py
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
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

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:

Ví dụ

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:

  • 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.
  • Tiếp theo chúng ta tạo object putin của class Person.
  • Chúng ta khai báo một hàm độc lập greeting() có thể in ra dòng thông báo.
  • Đ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.
  • 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.
  • 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 và 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#.

Bài viết này đã giúp ích cho bạn?

Advertisements