Thuật ngữ metaprogramming ám chỉ lập trình nơi chương trình máy tính cố gắng thao tác hoặc có kiến thức về chính nó. Python hỗ trợ metaprogramming cho classes thông qua một loại class mới gọi là metaclass.
Metaprogramming qua metaclasses trong Python là xây dựng hàm và class thao tác code bằng cách sửa đổi, bao bọc (wrapping) hoặc sinh code hiện có.
Đặc điểm chính của metaprogramming:
- Metaclasses
- Decorators
- Class-decorators
Metaclass là gì?
Định nghĩa gọn: một class tạo ra class khác. Python là ngôn ngữ hướng đối tượng, mọi thứ là đối tượng, class cũng không ngoại lệ. Định nghĩa class với từ khóa class, Python thực thi nó và tạo ra một đối tượng. Vì là đối tượng, có thể gán, sao chép, thêm thuộc tính, làm tham số hàm, v.v. Sử dụng metaclasses cho phép kiểm soát việc sinh class, như sửa đổi thuộc tính class, kiểm tra tính hợp lệ của thuộc tính, v.v.
Metaclass tích hợp của Python
Metaclass của tất cả các class là type.
class a: pass
print(type(a))
<class 'type'>
Từ kết quả: "a" (class) là đối tượng của class type. Có thể nói "a" là instance của class type. Vậy type là một metaclass. Trong Python, mọi class đều thuộc metaclass tích hợp type.
Sử dụng Metaclass để tạo class Python
Như ví dụ trên, type là metaclass mặc định. Có thể tạo class mới với metaclass mặc định này.
>>> class hello(object): pass
Có thể tạo thủ công:
>>> hello = type('hello', (), {})
>>> print(hello) # trả về class object
>>> print(hello()) # tạo instance từ class
<__main__.hello object at 0x05D86830>
type chấp nhận dictionary để định nghĩa thuộc tính class:
>>> class hello(object):
... world = True
Tương đương:
hello = type('hello', (), {'world': True})
Tạo Metaclass tùy chỉnh
Khi nào tạo metaclass? Khi muốn kiểm soát việc tạo class, như xác thực hoặc sửa đổi thuộc tính class.
Quy trình khởi tạo class:
- Tạo instance bằng cách gọi
__new__() - Gọi
__init__()để khởi tạo instance vừa tạo
Khi tạo custom metaclass, thực chất thay đổi phương thức __new__() hoặc __init__ của class cha.
Ví dụ 1: Metaclass sửa đổi thuộc tính class
class LowercaseMeta(type):
def __new__(mcs, name, bases, attrs):
lower_attrs = {}
for k, v in attrs.items():
if not k.startswith('__'):
lower_attrs[k.lower()] = v
else:
lower_attrs[k] = v
return type.__new__(mcs, name, bases, lower_attrs)
class LowercaseClass(metaclass=LowercaseMeta):
BAR = True
def HELLO(self):
print('hello')
print(dir(LowercaseClass))
LowercaseClass().hello()
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'hello']
hello
Ví dụ 2: Thêm thuộc tính bổ sung cho class
class ListMeta(type):
def __new__(mcs, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(mcs, name, bases, attrs)
class MyList(list, metaclass=ListMeta):
pass
l = MyList()
l.add(1)
print(l)
# Tương tự với class decorator
def class_decorator(cls):
cls.add = lambda self, value: self.append(value)
return cls
@class_decorator
class MyList(list):
pass
l = MyList()
l.append(1)
print(l)
[1]
[1]
Thuộc tính __metaclass__
Trong Python, nếu một class hoặc một trong các base class có thuộc tính __metaclass__, nó được dùng làm metaclass, ngược lại type là metaclass.
Khi định nghĩa class như:
class hello(object):
x = 10
Class hello không có __metaclass__, nên dùng type, tạo class như: hello = type(name, bases, dict).
Nếu hello có metaclass:
class hello(object):
__metaclass__ = myMetaClass
pass
Lúc này tạo class bằng: Hello = myMetaClass(name, bases, dict).