Giao diện
Metaclasses Expert
"Metaclasses are deeper magic than 99% of users should ever worry about" — Tim Peters
Learning Outcomes
Sau khi hoàn thành trang này, bạn sẽ:
- ✅ Hiểu
type()function và cách Python tạo classes - ✅ Phân biệt
__new__vs__init__trong metaclasses - ✅ So sánh Class Decorators vs Metaclasses
- ✅ Implement metaclasses cho validation, registration, và ORMs
- ✅ Biết khi nào KHÔNG nên dùng metaclasses
- ✅ Tránh các production pitfalls phổ biến
type() - Nền tảng của Metaclasses
Trong Python, mọi thứ đều là object, kể cả classes. Và type là metaclass mặc định của tất cả classes.
python
# type() có 2 cách dùng:
# 1. Kiểm tra type của object
print(type(42)) # <class 'int'>
print(type("hello")) # <class 'str'>
# 2. Tạo class động
class User:
name = "default"
def greet(self):
return f"Hello, {self.name}"
# Tương đương với:
User = type(
'User', # Tên class
(), # Base classes (tuple)
{ # Namespace (dict)
'name': 'default',
'greet': lambda self: f"Hello, {self.name}"
}
)
# Cả hai cách đều tạo ra class giống hệt nhau!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Class là Instance của type
python
class MyClass:
pass
# MyClass là instance của type
print(type(MyClass)) # <class 'type'>
print(isinstance(MyClass, type)) # True
# Và type là instance của chính nó!
print(type(type)) # <class 'type'>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Hierarchy
type (metaclass)
↓ creates
MyClass (class)
↓ creates
my_instance (object)1
2
3
4
5
2
3
4
5
Tạo Custom Metaclass
Metaclass là class kế thừa từ type.
python
class MyMeta(type):
"""Custom metaclass."""
def __new__(mcs, name, bases, namespace):
"""
Được gọi TRƯỚC khi class được tạo.
Args:
mcs: Metaclass (MyMeta)
name: Tên class đang được tạo
bases: Tuple các base classes
namespace: Dict chứa attributes và methods
Returns:
Class object mới
"""
print(f"Creating class: {name}")
print(f"Bases: {bases}")
print(f"Namespace keys: {list(namespace.keys())}")
# Tạo class bằng cách gọi type.__new__
cls = super().__new__(mcs, name, bases, namespace)
return cls
def __init__(cls, name, bases, namespace):
"""
Được gọi SAU khi class đã được tạo.
Args:
cls: Class vừa được tạo
name, bases, namespace: Giống __new__
"""
print(f"Initializing class: {name}")
super().__init__(name, bases, namespace)
# Sử dụng metaclass
class User(metaclass=MyMeta):
name = "default"
def greet(self):
return f"Hello, {self.name}"
# Output:
# Creating class: User
# Bases: ()
# Namespace keys: ['__module__', '__qualname__', 'name', 'greet']
# Initializing class: User1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
__new__ vs __init__ trong Metaclass
| Method | Khi nào gọi | Mục đích | Return |
|---|---|---|---|
__new__ | Trước khi class tồn tại | Tạo và customize class | Class object |
__init__ | Sau khi class đã tạo | Post-processing | None |
python
class Meta(type):
def __new__(mcs, name, bases, namespace):
# Có thể modify namespace TRƯỚC khi class được tạo
namespace['created_by'] = 'Meta.__new__'
# Có thể thay đổi bases
# bases = bases + (SomeMixin,)
# Có thể thay đổi name
# name = 'Modified' + name
return super().__new__(mcs, name, bases, namespace)
def __init__(cls, name, bases, namespace):
# Class đã tồn tại, chỉ có thể thêm attributes
cls.initialized_by = 'Meta.__init__'
super().__init__(name, bases, namespace)
class MyClass(metaclass=Meta):
pass
print(MyClass.created_by) # 'Meta.__new__'
print(MyClass.initialized_by) # 'Meta.__init__'1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Use Case 1: Auto-Registration
python
class PluginMeta(type):
"""Metaclass tự động đăng ký plugins."""
_registry: dict[str, type] = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
# Không đăng ký base class
if bases: # Chỉ đăng ký subclasses
plugin_name = namespace.get('name', name.lower())
mcs._registry[plugin_name] = cls
print(f"Registered plugin: {plugin_name}")
return cls
@classmethod
def get_plugin(mcs, name: str):
return mcs._registry.get(name)
@classmethod
def list_plugins(mcs):
return list(mcs._registry.keys())
class Plugin(metaclass=PluginMeta):
"""Base class cho plugins."""
name: str = ""
class AudioPlugin(Plugin):
name = "audio"
def process(self):
return "Processing audio..."
class VideoPlugin(Plugin):
name = "video"
def process(self):
return "Processing video..."
# Output:
# Registered plugin: audio
# Registered plugin: video
print(PluginMeta.list_plugins()) # ['audio', 'video']
print(PluginMeta.get_plugin('audio')) # <class 'AudioPlugin'>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Use Case 2: Attribute Validation
python
class ValidatedMeta(type):
"""Metaclass kiểm tra class có đủ required attributes."""
required_attrs = ['name', 'version']
def __new__(mcs, name, bases, namespace):
# Bỏ qua base class
if bases:
for attr in mcs.required_attrs:
if attr not in namespace:
raise TypeError(
f"Class {name} missing required attribute: {attr}"
)
return super().__new__(mcs, name, bases, namespace)
class Component(metaclass=ValidatedMeta):
"""Base class yêu cầu name và version."""
pass
class Button(Component):
name = "Button"
version = "1.0.0"
def render(self):
return f"<button>{self.name}</button>"
# ❌ TypeError: Class BadComponent missing required attribute: version
class BadComponent(Component):
name = "Bad"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Use Case 3: ORM-Style Field Collection
python
class Field:
"""Base class cho ORM fields."""
def __init__(self, field_type: type, required: bool = True):
self.field_type = field_type
self.required = required
self.name = None # Set by metaclass
def validate(self, value):
if value is None and self.required:
raise ValueError(f"{self.name} is required")
if value is not None and not isinstance(value, self.field_type):
raise TypeError(
f"{self.name}: expected {self.field_type.__name__}, "
f"got {type(value).__name__}"
)
return value
class StringField(Field):
def __init__(self, max_length: int = 255, **kwargs):
super().__init__(str, **kwargs)
self.max_length = max_length
def validate(self, value):
value = super().validate(value)
if value and len(value) > self.max_length:
raise ValueError(
f"{self.name}: max length is {self.max_length}"
)
return value
class IntField(Field):
def __init__(self, min_value: int = None, max_value: int = None, **kwargs):
super().__init__(int, **kwargs)
self.min_value = min_value
self.max_value = max_value
class ModelMeta(type):
"""Metaclass thu thập Fields và tạo __init__."""
def __new__(mcs, name, bases, namespace):
# Thu thập fields
fields = {}
for key, value in list(namespace.items()):
if isinstance(value, Field):
value.name = key
fields[key] = value
namespace['_fields'] = fields
# Tạo __init__ tự động
def __init__(self, **kwargs):
for field_name, field in self._fields.items():
value = kwargs.get(field_name)
validated = field.validate(value)
setattr(self, field_name, validated)
namespace['__init__'] = __init__
return super().__new__(mcs, name, bases, namespace)
class Model(metaclass=ModelMeta):
"""Base class cho ORM models."""
def to_dict(self):
return {name: getattr(self, name) for name in self._fields}
# Sử dụng
class User(Model):
name = StringField(max_length=100)
age = IntField(min_value=0, required=False)
email = StringField()
user = User(name="HPN", email="hpn@example.com", age=28)
print(user.to_dict()) # {'name': 'HPN', 'age': 28, 'email': 'hpn@example.com'}
# Validation hoạt động
User(name="", email="test@test.com") # OK
User(name="A" * 200, email="x") # ValueError: name: max length is 100
User(email="test@test.com") # ValueError: name is required1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Class Decorators vs Metaclasses
Khi nào dùng Class Decorator
python
def add_repr(cls):
"""Class decorator thêm __repr__."""
def __repr__(self):
attrs = ', '.join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{cls.__name__}({attrs})"
cls.__repr__ = __repr__
return cls
@add_repr
class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
print(User("HPN", 28)) # User(name='HPN', age=28)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Khi nào dùng Metaclass
python
class SingletonMeta(type):
"""Metaclass đảm bảo chỉ có 1 instance."""
_instances: dict = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self, url: str):
self.url = url
print(f"Connecting to {url}...")
db1 = Database("postgresql://localhost") # Connecting...
db2 = Database("mysql://localhost") # Không in gì - dùng instance cũ
print(db1 is db2) # True
))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
So sánh
| Tiêu chí | Class Decorator | Metaclass |
|---|---|---|
| Độ phức tạp | Đơn giản | Phức tạp |
| Kế thừa | Không tự động | Tự động kế thừa |
| Thời điểm | Sau khi class tạo | Trong quá trình tạo |
| Use case | Thêm methods, modify | Control class creation |
| Debugging | Dễ | Khó |
Quy tắc chọn
python
# ✅ Dùng Class Decorator khi:
# - Thêm/modify methods hoặc attributes
# - Không cần affect subclasses
# - Logic đơn giản
# ✅ Dùng Metaclass khi:
# - Cần control class creation
# - Cần affect tất cả subclasses
# - Cần modify class TRƯỚC khi nó tồn tại
# - Implementing frameworks (ORMs, validation)
# ✅ Dùng __init_subclass__ khi:
# - Cần hook vào subclass creation
# - Không cần full metaclass power
# - Python 3.6+1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__init_subclass__ - Alternative đơn giản
Python 3.6+ cung cấp __init_subclass__ như alternative nhẹ hơn metaclass.
python
class Plugin:
_registry: dict[str, type] = {}
def __init_subclass__(cls, plugin_name: str = None, **kwargs):
super().__init_subclass__(**kwargs)
name = plugin_name or cls.__name__.lower()
Plugin._registry[name] = cls
class AudioPlugin(Plugin, plugin_name="audio"):
pass
class VideoPlugin(Plugin): # Dùng tên class
pass
print(Plugin._registry) # {'audio': AudioPlugin, 'videoplug': VideoPlugin}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
So sánh với Metaclass
python
# Metaclass - Mạnh hơn nhưng phức tạp
class PluginMeta(type):
_registry = {}
def __new__(mcs, name, bases, namespace, plugin_name=None, **kwargs):
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
if bases:
mcs._registry[plugin_name or name.lower()] = cls
return cls
# __init_subclass__ - Đơn giản hơn, đủ cho hầu hết cases
class Plugin:
_registry = {}
def __init_subclass__(cls, plugin_name=None, **kwargs):
super().__init_subclass__(**kwargs)
Plugin._registry[plugin_name or cls.__name__.lower()] = cls1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Production Pitfalls 🚨
Pitfall 1: Metaclass Conflict
python
class Meta1(type):
pass
class Meta2(type):
pass
class A(metaclass=Meta1):
pass
class B(metaclass=Meta2):
pass
# ❌ TypeError: metaclass conflict
class C(A, B):
pass
# ✅ FIX: Tạo metaclass kế thừa cả hai
class CombinedMeta(Meta1, Meta2):
pass
class C(A, B, metaclass=CombinedMeta):
pass1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Pitfall 2: Quên gọi super()
python
# ❌ BUG: Không gọi super().__new__
class BadMeta(type):
def __new__(mcs, name, bases, namespace):
namespace['added'] = True
# Quên return super().__new__()!
# Return None → class không được tạo
# ✅ FIX: Luôn gọi super()
class GoodMeta(type):
def __new__(mcs, name, bases, namespace):
namespace['added'] = True
return super().__new__(mcs, name, bases, namespace)1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Pitfall 3: Modify namespace sau khi class tạo
python
# ❌ BUG: Modify namespace trong __init__ không có effect
class BadMeta(type):
def __init__(cls, name, bases, namespace):
namespace['late_add'] = True # Không có effect!
super().__init__(name, bases, namespace)
class Test(metaclass=BadMeta):
pass
print(hasattr(Test, 'late_add')) # False!
# ✅ FIX: Modify trong __new__ hoặc set trực tiếp trên cls
class GoodMeta(type):
def __init__(cls, name, bases, namespace):
cls.late_add = True # Set trực tiếp trên class
super().__init__(name, bases, namespace)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Pitfall 4: Infinite recursion với __call__
python
# ❌ BUG: Infinite recursion
class BadMeta(type):
def __call__(cls, *args, **kwargs):
print("Creating instance...")
return cls(*args, **kwargs) # Gọi lại __call__!
# ✅ FIX: Gọi super().__call__
class GoodMeta(type):
def __call__(cls, *args, **kwargs):
print("Creating instance...")
return super().__call__(*args, **kwargs)1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Pitfall 5: Overcomplicating với Metaclass
python
# ❌ OVERKILL: Dùng metaclass cho việc đơn giản
class AddReprMeta(type):
def __new__(mcs, name, bases, namespace):
def __repr__(self):
return f"{name}(...)"
namespace['__repr__'] = __repr__
return super().__new__(mcs, name, bases, namespace)
# ✅ BETTER: Dùng class decorator
def add_repr(cls):
cls.__repr__ = lambda self: f"{cls.__name__}(...)"
return cls
# ✅ EVEN BETTER: Dùng dataclass
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
# __repr__ được tạo tự động!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Khi nào KHÔNG dùng Metaclass
🚨 TRÁNH METACLASS KHI
- Class decorator đủ dùng - Đơn giản hơn nhiều
__init_subclass__đủ dùng - Python 3.6+ alternative- Chỉ cần thêm methods - Dùng mixin hoặc decorator
- Team không quen - Metaclass khó debug và maintain
- Không có use case rõ ràng - "Cool" không phải lý do
Decision Tree
Cần customize class creation?
├── Không → Không cần metaclass
└── Có → Cần affect subclasses?
├── Không → Dùng class decorator
└── Có → Cần modify TRƯỚC khi class tồn tại?
├── Không → Dùng __init_subclass__
└── Có → Dùng metaclass1
2
3
4
5
6
7
2
3
4
5
6
7
Bảng Tóm tắt
python
# === TẠO CLASS ĐỘNG ===
MyClass = type('MyClass', (Base,), {'attr': value})
# === CUSTOM METACLASS ===
class MyMeta(type):
def __new__(mcs, name, bases, namespace):
# Modify trước khi class tạo
return super().__new__(mcs, name, bases, namespace)
def __init__(cls, name, bases, namespace):
# Post-processing sau khi class tạo
super().__init__(name, bases, namespace)
def __call__(cls, *args, **kwargs):
# Control instance creation
return super().__call__(*args, **kwargs)
class MyClass(metaclass=MyMeta):
pass
# === ALTERNATIVE: __init_subclass__ ===
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# Hook vào subclass creation
# === ALTERNATIVE: Class Decorator ===
def decorator(cls):
cls.added = True
return cls
@decorator
class MyClass:
pass1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34