Tạo Metaclass Tùy chỉnh
Trong Python, ta có thể tùy biến quy trình tạo lớp bằng cách truyền từ khóa metaclass trong định nghĩa lớp. Điều này cũng có thể thực hiện bằng cách kế thừa từ một lớp đã truyền sẵn từ khóa này.
class MyMeta(type):
pass
class MyClass(metaclass=MyMeta):
pass
class MySubclass(MyClass):
pass
Ta có thể thấy bên dưới rằng kiểu của lớp MyMeta là type và kiểu của MyClass và MySubClass là MyMeta.
print(type(MyMeta))
print(type(MyClass))
print(type(MySubclass))
<class 'type'>
<class '__main__.MyMeta'>
<class '__main__.MyMeta'>
Khi định nghĩa một lớp mà không chỉ định metaclass, metaclass mặc định type sẽ được dùng. Nếu một metaclass được cung cấp và nó không phải là thể hiện của type(), thì nó sẽ được dùng trực tiếp làm metaclass.
__new__ và __init__
Metaclass cũng có thể được định nghĩa theo một trong hai cách dưới đây. Chúng ta sẽ giải thích sự khác nhau giữa chúng ngay sau đó.
class MetaOne(type):
def __new__(cls, name, bases, dict):
pass
class MetaTwo(type):
def __init__(self, name, bases, dict):
pass
__new__ được dùng khi muốn xác định dict hoặc tuple bases trước khi lớp được tạo. Giá trị trả về của __new__ thường là một thể hiện của cls. __new__ cho phép các lớp con của kiểu bất biến tùy biến việc tạo thể hiện. Nó có thể được ghi đè trong metaclass tùy chỉnh để tùy biến việc tạo lớp. __init__ thường được gọi sau khi đối tượng đã được tạo nhằm khởi tạo nó.
Phương thức __call__ của Metaclass
Theo tài liệu chính thức, ta cũng có thể ghi đè các phương thức lớp khác bằng cách định nghĩa phương thức __call__() tùy chỉnh trong metaclass để cho phép hành vi tùy biến khi lớp được gọi.
Phương thức __prepare__ của Metaclass
Theo tài liệu mô hình dữ liệu của Python
Sau khi metaclass thích hợp đã được xác định, không gian tên của lớp sẽ được chuẩn bị. Nếu metaclass có thuộc tính __prepare__, nó được gọi như namespace = metaclass.__prepare__(name, bases, **kwds) (trong đó các đối số từ khóa bổ sung, nếu có, đến từ định nghĩa lớp). Nếu metaclass không có thuộc tính __prepare__, không gian tên của lớp được khởi tạo như một ánh xạ có thứ tự rỗng. - docs.python.org
Thiết kế Singleton bằng Metaclass
Đây là một mẫu thiết kế giới hạn việc khởi tạo một lớp chỉ còn một đối tượng. Điều này có thể hữu ích, chẳng hạn khi thiết kế lớp kết nối đến cơ sở dữ liệu. Bạn có thể muốn chỉ có một thể hiện của lớp kết nối.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(SingletonMeta,cls).__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
pass
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu metaclass là gì và cách triển khai chúng trong lập trình Python. Metaclass có thể được áp dụng vào ghi log, đăng ký lớp tại thời điểm tạo và profiling, cùng nhiều mục đích khác. Chúng có vẻ là khái niệm khá trừu tượng, và bạn có thể tự hỏi liệu mình có cần dùng chúng hay không. Lập trình viên Python kỳ cựu Tim Peters trả lời câu hỏi đó một cách súc tích.
"Metaclass là phép thuật sâu hơn mức 99% người dùng cần bận tâm. Nếu bạn băn khoăn liệu mình có cần chúng không, thì là không (những người thực sự cần sẽ biết chắc họ cần, và không cần lời giải thích vì sao).” - realpython
Nếu bạn muốn tìm hiểu thêm về Python, hãy bắt đầu lộ trình kỹ năng Python Programming của DataCamp và xem Hướng dẫn Python Classes của chúng tôi.
Metaclass trong Python là lớp của một lớp, định nghĩa cách một lớp hoạt động. Bản thân một lớp là một thể hiện của metaclass. Một lớp trong Python định nghĩa cách các thể hiện của lớp đó sẽ hoạt động. Để hiểu rõ metaclass, bạn cần có kinh nghiệm làm việc với các lớp trong Python. Trước khi đi sâu vào metaclass, hãy cùng làm rõ một vài khái niệm.
Mọi thứ trong Python đều là Đối tượng
Lớp trong Python có thể được tạo động
typetrong Python cho phép chúng ta kiểm tra kiểu của một đối tượng. Ta có thể tiếp tục kiểm tra kiểu của đối tượng đã tạo ở trên.Khoan, chuyện gì vừa xảy ra vậy? Ta mong đợi kiểu của đối tượng đã tạo ở trên là class, nhưng không phải. Hãy giữ suy nghĩ đó, chúng ta sẽ bàn tiếp sau. Ta cũng nhận thấy kiểu của chính
typelàtype. Nó là một thể hiện củatype. Một điều “kỳ diệu” khác màtypelàm được là cho phép ta tạo lớp một cách động. Hãy xem cách thực hiện bên dưới. LớpDataCampdưới đây sẽ được tạo như sau bằngtype:Trong ví dụ trên,
DataCamplà tên lớp, cònDataCampClasslà biến giữ tham chiếu đến lớp. Khi dùngtypeta có thể truyền các thuộc tính của lớp thông qua một dictionary như sau:Nếu muốn
PythonClasskế thừa từ lớpDataCamp, ta truyền nó vào đối số thứ hai khi định nghĩa lớp bằngtypeGiờ khi đã loại bỏ hai khái niệm đó, ta nhận ra rằng Python tạo các lớp bằng một metaclass. Chúng ta đã thấy mọi thứ trong Python là một đối tượng, và các đối tượng này được tạo bởi metaclass. Bất cứ khi nào ta gọi
classđể tạo lớp, sẽ có một metaclass đứng sau làm phép để tạo ra lớp đó. Chúng ta đã thấytypelàm điều này ở trên. Nó tương tự nhưstrtạo chuỗi vàinttạo số nguyên. Trong Python, thuộc tính___class__cho phép ta kiểm tra kiểu của thể hiện hiện tại. Hãy tạo một chuỗi bên dưới và kiểm tra kiểu của nó.Ta cũng có thể kiểm tra kiểu bằng
type(article).Khi kiểm tra kiểu của
str, ta cũng thấy nó là type.Khi kiểm tra kiểu của
float,int,list,tuplevàdict, ta sẽ nhận được kết quả tương tự. Bởi vì tất cả các đối tượng này đều có kiểu làtype.Chúng ta đã thấy
typetạo ra các lớp. Do đó khi kiểm tra__class__của__class__nó sẽ trả vềtype.