Skip to content

Module dataclasses — Định nghĩa data class không boilerplate

Mỗi lần tạo một class chứa dữ liệu trong Python, bạn lại viết đi viết lại cùng một đoạn __init__, __repr__, __eq__ — đôi khi lên tới 30-50 dòng chỉ để khai báo 5 thuộc tính. Trong một hệ thống microservice với hàng chục DTO (Data Transfer Object), config object, và domain model, boilerplate này nhân lên thành hàng nghìn dòng code không mang giá trị logic nào.

Module dataclasses (có sẵn từ Python 3.7) giải quyết triệt để vấn đề này. Với một decorator @dataclass, Python tự sinh toàn bộ các dunder method cần thiết — bạn chỉ cần khai báo field và kiểu dữ liệu. Kết quả: class 50 dòng rút gọn còn 5 dòng, giữ nguyên type safety và khả năng mở rộng.

Bài viết này đi sâu vào mọi khía cạnh của dataclasses — từ cú pháp cơ bản đến frozen, slots, kế thừa, và so sánh trực tiếp với attrsPydantic để bạn chọn đúng công cụ cho từng bài toán production.

Bức tranh tư duy

Hãy hình dung @dataclass như tờ khai mẫu hành chính. Khi bạn đến cơ quan nhà nước, bạn không cần viết đơn từ đầu — bạn chỉ cần điền vào các ô trống trên mẫu đơn có sẵn. Tương tự, @dataclass là mẫu đơn: bạn khai báo tên field và kiểu dữ liệu, Python tự động điền vào các "thủ tục" (__init__, __repr__, __eq__, ...).

Bạn khai báo:                  Python tự sinh:
┌─────────────────────┐        ┌──────────────────────────┐
│ @dataclass           │        │ __init__(self, ...)       │
│ class User:          │ ────▶  │ __repr__(self)            │
│     name: str        │        │ __eq__(self, other)       │
│     email: str       │        │ __hash__(self)  (*)       │
│     age: int = 0     │        │ __lt__, __le__,... (**)   │
└─────────────────────┘        └──────────────────────────┘
                                (*) khi frozen=True
                                (**) khi order=True

Giới hạn của phép so sánh: tờ khai mẫu chỉ xử lý quy trình chuẩn. Nếu bạn cần validation phức tạp, coercion kiểu dữ liệu, hoặc serialization tự động sang JSON — đó là lúc bạn cần thêm __post_init__, hoặc cân nhắc chuyển sang attrs / Pydantic.

Cốt lõi kỹ thuật

@dataclass cơ bản

Decorator @dataclass nhận nhiều tham số điều khiển hành vi sinh code:

python
from dataclasses import dataclass

@dataclass(
    init=True,         # Sinh __init__
    repr=True,         # Sinh __repr__
    eq=True,           # Sinh __eq__ và __ne__
    order=False,       # Sinh __lt__, __le__, __gt__, __ge__
    unsafe_hash=False, # Sinh __hash__ (nguy hiểm với mutable)
    frozen=False,      # Bất biến — cấm gán lại attribute
    match_args=True,   # Hỗ trợ structural pattern matching (3.10+)
    kw_only=False,     # Mọi field đều keyword-only (3.10+)
    slots=False,       # Dùng __slots__ thay __dict__ (3.10+)
)
class User:
    name: str
    email: str
    age: int = 0

Ví dụ đơn giản nhất — chỉ cần 4 dòng để thay thế 25 dòng class truyền thống:

python
from dataclasses import dataclass

@dataclass
class Product:
    name: str
    price: float
    quantity: int = 0

p = Product("Laptop", 25_000_000, 5)
print(p)       # Product(name='Laptop', price=25000000, quantity=5)
p == Product("Laptop", 25_000_000, 5)  # True — so sánh theo giá trị

Khi bật order=True, Python sinh các phương thức so sánh dựa trên thứ tự khai báo field:

python
@dataclass(order=True)
class Priority:
    level: int
    name: str

tasks = [Priority(3, "Low"), Priority(1, "Critical"), Priority(2, "Medium")]
sorted(tasks)
# [Priority(level=1, name='Critical'),
#  Priority(level=2, name='Medium'),
#  Priority(level=3, name='Low')]

field() — Kiểm soát từng thuộc tính

Hàm field() cho phép tinh chỉnh hành vi của từng field riêng lẻ:

python
from dataclasses import dataclass, field
from typing import List

@dataclass
class Team:
    name: str
    members: List[str] = field(default_factory=list)  # Mutable default an toàn
    _internal_id: str = field(repr=False, compare=False)  # Ẩn khỏi repr và eq
    created_by: str = field(kw_only=True)  # Bắt buộc truyền keyword (3.10+)

Bảng tham số field():

Tham sốMặc địnhMô tả
defaultMISSINGGiá trị mặc định
default_factoryMISSINGCallable trả về default (cho mutable)
initTrueCó xuất hiện trong __init__ không
reprTrueCó xuất hiện trong __repr__ không
compareTrueTham gia __eq__order không
hashNoneTham gia __hash__ (None = theo compare)
kw_onlyFalseBắt buộc keyword-only (3.10+)
metadataNoneDict metadata tùy chỉnh

Ví dụ field(init=False) cho computed field:

python
@dataclass
class Invoice:
    items: List[float]
    tax_rate: float = 0.1
    total: float = field(init=False)  # Tính sau, không truyền vào __init__

    def __post_init__(self):
        subtotal = sum(self.items)
        self.total = subtotal * (1 + self.tax_rate)

inv = Invoice([100_000, 200_000, 50_000])
print(inv.total)  # 385000.0

post_init — Validation, normalization, computed fields

__post_init__ được gọi tự động sau __init__, là nơi lý tưởng để validate dữ liệu và tính toán field phụ thuộc:

python
from dataclasses import dataclass, field, InitVar

@dataclass
class Employee:
    name: str
    email: str
    department: str
    salary: float
    password: InitVar[str]  # Chỉ dùng trong __init__, không lưu trữ
    password_hash: str = field(init=False, repr=False)

    def __post_init__(self, password: str):
        # Validation
        if self.salary < 0:
            raise ValueError(f"Lương không thể âm: {self.salary}")
        if "@" not in self.email:
            raise ValueError(f"Email không hợp lệ: {self.email}")

        # Normalization
        self.name = self.name.strip().title()
        self.email = self.email.strip().lower()

        # Computed field từ InitVar
        import hashlib
        self.password_hash = hashlib.sha256(password.encode()).hexdigest()

emp = Employee("  nguyen van a  ", "NVA@Corp.com", "Engineering", 15_000_000, "s3cret")
print(emp.name)   # "Nguyen Van A"
print(emp.email)  # "nva@corp.com"
# password không tồn tại trên emp — chỉ password_hash được lưu

InitVar[T] đặc biệt hữu ích khi bạn cần truyền dữ liệu vào constructor nhưng không muốn lưu nó thành attribute — ví dụ password, raw config string, hoặc temporary context.

frozen=True — Dataclass bất biến

frozen=True biến dataclass thành đối tượng bất biến (immutable). Mọi nỗ lực gán lại attribute đều ném FrozenInstanceError:

python
from dataclasses import dataclass, replace

@dataclass(frozen=True)
class Coordinate:
    lat: float
    lon: float

hanoi = Coordinate(21.0285, 105.8542)
# hanoi.lat = 10.0  # FrozenInstanceError!

# Tạo bản sao với giá trị mới bằng replace()
hcm = replace(hanoi, lat=10.8231, lon=106.6297)
print(hcm)  # Coordinate(lat=10.8231, lon=106.6297)

Ưu điểm quan trọng: frozen dataclass tự động hashable, có thể dùng làm dict key hoặc phần tử set:

python
@dataclass(frozen=True)
class CacheKey:
    endpoint: str
    params_hash: str

cache: dict[CacheKey, bytes] = {}
key = CacheKey("/api/users", "abc123")
cache[key] = b'{"users": []}'  # Hợp lệ vì CacheKey hashable

slots=True — Tối ưu bộ nhớ (Python 3.10+)

Khi slots=True, Python sinh __slots__ thay vì dùng __dict__, giảm đáng kể bộ nhớ trên mỗi instance:

python
from dataclasses import dataclass
import sys

@dataclass
class PointDict:
    x: float
    y: float

@dataclass(slots=True)
class PointSlots:
    x: float
    y: float

pd = PointDict(1.0, 2.0)
ps = PointSlots(1.0, 2.0)
print(sys.getsizeof(pd.__dict__))  # ~104 bytes
# ps không có __dict__
# hasattr(ps, '__dict__')  # False

# Benchmark bộ nhớ với 100_000 instances:
# PointDict:  ~16.8 MB
# PointSlots: ~8.8 MB  (giảm ~48%)

Lưu ý: slots=True không cho phép thêm attribute động. Nếu bạn cần flexibility, giữ slots=False.

Kế thừa (Inheritance)

Khi kế thừa dataclass, field của class cha đứng trước field class con trong __init__. Quy tắc quan trọng: field có default phải đứng sau field không có default.

python
from dataclasses import dataclass

@dataclass
class BaseConfig:
    env: str
    debug: bool = False

@dataclass
class DatabaseConfig(BaseConfig):
    host: str = "localhost"
    port: int = 5432
    name: str = "app_db"

# __init__ nhận: env, debug=False, host="localhost", port=5432, name="app_db"
db = DatabaseConfig(env="production")

Khi cả cha và con đều có __post_init__, bắt buộc gọi super().__post_init__():

python
@dataclass
class BaseModel:
    id: int

    def __post_init__(self):
        if self.id <= 0:
            raise ValueError(f"ID phải dương: {self.id}")

@dataclass
class UserModel(BaseModel):
    name: str
    email: str

    def __post_init__(self):
        super().__post_init__()  # Gọi validation của BaseModel
        self.name = self.name.strip()

Thực chiến

Xây dựng hệ thống Configuration cho microservice

Bài toán thực tế: một microservice cần quản lý cấu hình database, cache, và logging — tất cả đều immutable sau khi load, có validation, và hỗ trợ serialization.

python
from dataclasses import dataclass, field, asdict, replace
from typing import Optional
import json


@dataclass(frozen=True)
class DatabaseConfig:
    host: str
    port: int = 5432
    name: str = "app_db"
    max_connections: int = 20
    ssl_enabled: bool = True

    def __post_init__(self):
        if not (1 <= self.port <= 65535):
            raise ValueError(f"Port ngoài phạm vi: {self.port}")
        if self.max_connections < 1:
            raise ValueError(f"max_connections phải >= 1: {self.max_connections}")

    @property
    def connection_string(self) -> str:
        scheme = "postgresql+ssl" if self.ssl_enabled else "postgresql"
        return f"{scheme}://{self.host}:{self.port}/{self.name}"


@dataclass(frozen=True)
class CacheConfig:
    host: str = "localhost"
    port: int = 6379
    ttl_seconds: int = 300
    max_memory_mb: int = 256
    prefix: str = "app"

    def __post_init__(self):
        if self.ttl_seconds < 0:
            raise ValueError(f"TTL không thể âm: {self.ttl_seconds}")


@dataclass(frozen=True)
class LoggingConfig:
    level: str = "INFO"
    format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    file_path: Optional[str] = None

    def __post_init__(self):
        valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
        if self.level.upper() not in valid_levels:
            raise ValueError(f"Log level không hợp lệ: {self.level}")
        # frozen=True dùng object.__setattr__ để normalize
        object.__setattr__(self, "level", self.level.upper())


@dataclass(frozen=True)
class AppConfig:
    """Configuration tổng hợp cho toàn bộ microservice."""
    app_name: str
    version: str
    database: DatabaseConfig
    cache: CacheConfig = field(default_factory=CacheConfig)
    logging: LoggingConfig = field(default_factory=LoggingConfig)
    allowed_origins: tuple[str, ...] = ("https://app.example.com",)

    def to_json(self) -> str:
        """Serialize config ra JSON."""
        return json.dumps(asdict(self), indent=2, ensure_ascii=False)

    @classmethod
    def from_json(cls, raw: str) -> "AppConfig":
        """Factory method: load config từ JSON string."""
        data = json.loads(raw)
        return cls(
            app_name=data["app_name"],
            version=data["version"],
            database=DatabaseConfig(**data["database"]),
            cache=CacheConfig(**data.get("cache", {})),
            logging=LoggingConfig(**data.get("logging", {})),
            allowed_origins=tuple(data.get("allowed_origins", [])),
        )

    def with_overrides(self, **kwargs) -> "AppConfig":
        """Tạo config mới với một số giá trị thay đổi."""
        return replace(self, **kwargs)

Sử dụng trong production:

python
# Load config
config = AppConfig(
    app_name="order-service",
    version="2.1.0",
    database=DatabaseConfig(host="db.internal", port=5432, name="orders"),
)

print(config.database.connection_string)
# postgresql+ssl://db.internal:5432/orders

# Serialize
json_str = config.to_json()

# Override cho test environment
test_config = config.with_overrides(
    database=DatabaseConfig(host="localhost", port=5433, name="orders_test", ssl_enabled=False),
    logging=LoggingConfig(level="DEBUG"),
)

# Dùng làm cache key (hashable vì frozen=True)
config_cache: dict[AppConfig, dict] = {}
config_cache[config] = {"status": "loaded"}

Sai lầm điển hình

1. Mutable default value — Bug kinh điển

python
# ❌ SAI: list mặc định chia sẻ giữa mọi instance
@dataclass
class Team:
    name: str
    members: list = []  # ValueError tại class creation!
    # Python 3.7+ chặn lỗi này — nhưng dict, set vẫn cần cẩn thận

# ✅ ĐÚNG: Luôn dùng default_factory cho mutable
@dataclass
class Team:
    name: str
    members: list = field(default_factory=list)
    metadata: dict = field(default_factory=dict)

2. Quên super().post_init() khi kế thừa

python
@dataclass
class Base:
    id: int
    def __post_init__(self):
        if self.id < 0:
            raise ValueError("ID phải không âm")

# ❌ SAI: validation của Base bị bỏ qua hoàn toàn
@dataclass
class Child(Base):
    name: str
    def __post_init__(self):
        self.name = self.name.strip()

Child(-1, "test")  # Không raise! Bug tiềm ẩn

# ✅ ĐÚNG: Luôn gọi super().__post_init__()
@dataclass
class Child(Base):
    name: str
    def __post_init__(self):
        super().__post_init__()  # Chạy validation của Base trước
        self.name = self.name.strip()

3. unsafe_hash=True không hiểu hệ quả

python
# ❌ SAI: Hash thay đổi khi mutate → mất key trong dict
@dataclass(unsafe_hash=True)
class User:
    name: str
    scores: list = field(default_factory=list)

u = User("A")
cache = {u: "data"}
u.name = "B"         # Hash thay đổi!
print(cache.get(u))  # None — mất dữ liệu

# ✅ ĐÚNG: Dùng frozen=True, hoặc loại mutable field khỏi hash
@dataclass(frozen=True)
class User:
    name: str

4. asdict với field không serializable

python
from dataclasses import dataclass, asdict
from datetime import datetime
import json

@dataclass
class Event:
    name: str
    timestamp: datetime

evt = Event("deploy", datetime(2024, 6, 15, 10, 30))
# ❌ SAI: datetime không JSON-serializable
# json.dumps(asdict(evt))  # TypeError!

# ✅ ĐÚNG: Custom serializer
def serialize_dataclass(obj) -> dict:
    """Chuyển dataclass sang dict JSON-safe."""
    result = {}
    for k, v in asdict(obj).items():
        if isinstance(v, datetime):
            result[k] = v.isoformat()
        else:
            result[k] = v
    return result

json.dumps(serialize_dataclass(evt))  # OK

5. slots=True với class cha không có slots

python
# ❌ SAI: Cha không slots, con có slots → con vẫn có __dict__ từ cha
@dataclass
class Base:
    name: str

@dataclass(slots=True)
class Child(Base):
    age: int

c = Child("A", 25)
c.dynamic = "oops"  # Không lỗi! __dict__ từ Base vẫn tồn tại
# → Mất hoàn toàn lợi ích bộ nhớ của slots

# ✅ ĐÚNG: Cả chuỗi kế thừa đều dùng slots
@dataclass(slots=True)
class Base:
    name: str

@dataclass(slots=True)
class Child(Base):
    age: int

c = Child("A", 25)
# c.dynamic = "oops"  # AttributeError — đúng hành vi mong đợi

Under the Hood

Cơ chế sinh code

Khi Python xử lý @dataclass, decorator đọc type annotation của class, tạo source code cho các method dưới dạng string, rồi exec() chúng vào namespace của class:

python
# Pseudo-code minh họa cơ chế bên trong
def dataclass(cls):
    fields = extract_fields_from_annotations(cls)

    # Sinh __init__ dưới dạng string rồi exec
    init_code = f"def __init__(self, {', '.join(f.name for f in fields)}):\n"
    for f in fields:
        init_code += f"    self.{f.name} = {f.name}\n"

    exec(init_code, namespace)
    cls.__init__ = namespace["__init__"]
    # Tương tự cho __repr__, __eq__, ...
    return cls

Bạn có thể kiểm tra metadata nội bộ qua __dataclass_fields__:

python
from dataclasses import dataclass, fields

@dataclass
class Server:
    host: str
    port: int = 8080

# Dict chứa Field objects
print(Server.__dataclass_fields__)
# {'host': Field(name='host', type=<class 'str'>, default=MISSING, ...),
#  'port': Field(name='port', type=<class 'int'>, default=8080, ...)}

# Hoặc dùng hàm fields() để lấy tuple
for f in fields(Server):
    print(f"{f.name}: {f.type}, default={f.default}")

Benchmark bộ nhớ

python
import sys
from dataclasses import dataclass

class RegularPoint:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

@dataclass
class DataclassPoint:
    x: float
    y: float

@dataclass(slots=True)
class SlotsPoint:
    x: float
    y: float

# Bộ nhớ trên mỗi instance (bytes, CPython 3.12):
# RegularPoint:    ~152 (object + __dict__)
# DataclassPoint:  ~152 (tương đương — vẫn dùng __dict__)
# SlotsPoint:      ~56  (không __dict__, giảm ~63%)

So sánh dataclass vs attrs vs Pydantic

Đặc điểmdataclassattrsPydantic v2
Có sẵn trong stdlib❌ (cài thêm)❌ (cài thêm)
Validation tích hợp❌ (tự viết)✅ (validators)✅ (mạnh nhất)
Type coercion
JSON serializationCơ bản (asdict)cattrs✅ (tích hợp)
Hiệu năng khởi tạo⚡ Nhanh nhất⚡ Nhanh🐢 Chậm hơn (do validation)
Bộ nhớ (slots)✅ (3.10+)
Pattern matching✅ (match_args)
Hệ sinh thái pluginNhỏTrung bìnhLớn
Phù hợp choDTO đơn giản, configDomain model phức tạpAPI schema, form validation

Khi nào chọn gì:

  • dataclass: Khi bạn cần DTO nhẹ, không muốn thêm dependency, validation đơn giản trong __post_init__
  • attrs: Khi bạn cần validator mạnh hơn __post_init__, converter tích hợp, và vẫn muốn hiệu năng cao
  • Pydantic: Khi bạn xây API (FastAPI), cần type coercion tự động, JSON schema generation, và validation phức tạp

Checklist ghi nhớ

✅ Checklist triển khai

Khai báo cơ bản

  • [ ] Luôn dùng @dataclass thay vì viết __init__/__repr__/__eq__ thủ công
  • [ ] Khai báo type annotation cho mọi field — đây là bắt buộc, không phải tùy chọn
  • [ ] Đặt field có default value SAU field không có default

Mutable defaults & field()

  • [ ] Dùng field(default_factory=list) thay vì = [] cho mutable default
  • [ ] Dùng field(repr=False) cho field nhạy cảm (password hash, token)
  • [ ] Dùng field(compare=False) cho metadata không tham gia so sánh logic

Validation & Post-init

  • [ ] Đặt validation logic trong __post_init__, không viết __init__ riêng
  • [ ] Dùng InitVar[T] cho dữ liệu chỉ cần lúc khởi tạo (password, raw input)
  • [ ] Luôn gọi super().__post_init__() khi kế thừa dataclass có __post_init__

Immutability & Performance

  • [ ] Dùng frozen=True cho config object, cache key, và mọi value object
  • [ ] Dùng replace() thay vì mutate frozen dataclass
  • [ ] Dùng slots=True khi tạo nhiều instance (>10,000) để tiết kiệm bộ nhớ
  • [ ] Đảm bảo toàn bộ chuỗi kế thừa đều có slots=True nếu dùng slots

Serialization

  • [ ] Kiểm tra tất cả field đều JSON-serializable trước khi dùng asdict() + json.dumps()
  • [ ] Viết custom serializer cho datetime, Enum, Path, và các kiểu đặc biệt

Bài tập luyện tập

Bài 1: Money dataclass với validation

Tạo dataclass Money với các yêu cầu:

  • Field amount (Decimal) và currency (str, 3 ký tự uppercase)
  • Frozen — không cho phép thay đổi sau khởi tạo
  • Validate: amount >= 0, currency đúng 3 ký tự uppercase
  • Method __add__ chỉ cộng được cùng currency

🧠 Quiz

Câu hỏi: Tại sao nên dùng Decimal thay vì float cho tiền tệ?

A. Decimal nhanh hơn float B. float có lỗi làm tròn nhị phân (0.1 + 0.2 ≠ 0.3) C. Decimal chiếm ít bộ nhớ hơn D. Python bắt buộc dùng Decimal cho số thập phân

Đáp án: Bfloat dùng biểu diễn IEEE 754 nhị phân, gây sai số tích lũy trong tính toán tài chính. Decimal dùng biểu diễn thập phân chính xác.

Lời giải tham khảo
python
from dataclasses import dataclass
from decimal import Decimal


@dataclass(frozen=True)
class Money:
    amount: Decimal
    currency: str

    def __post_init__(self):
        if not isinstance(self.amount, Decimal):
            object.__setattr__(self, "amount", Decimal(str(self.amount)))
        if self.amount < 0:
            raise ValueError(f"Số tiền không thể âm: {self.amount}")
        if len(self.currency) != 3 or not self.currency.isalpha() or not self.currency.isupper():
            raise ValueError(f"Currency phải là 3 ký tự uppercase: {self.currency}")

    def __add__(self, other: "Money") -> "Money":
        if not isinstance(other, Money):
            return NotImplemented
        if self.currency != other.currency:
            raise ValueError(f"Không thể cộng {self.currency} với {other.currency}")
        return Money(self.amount + other.amount, self.currency)

    def __str__(self) -> str:
        return f"{self.amount:,.2f} {self.currency}"


# Kiểm thử
price = Money(Decimal("150000"), "VND")
tax = Money(Decimal("15000"), "VND")
total = price + tax
print(total)  # 165,000.00 VND

# Frozen — không mutate được
# price.amount = Decimal("0")  # FrozenInstanceError!

# Validation
# Money(Decimal("-1"), "VND")  # ValueError
# Money(Decimal("100"), "usd")  # ValueError

Bài 2: Immutable config với replace()

Xây dựng hệ thống config cho ứng dụng web với:

  • ServerConfig(host, port, workers, debug) — frozen
  • AppConfig(name, version, server) — frozen, nested dataclass
  • Method for_testing() trả về config mới với debug=True, workers=1
  • Method for_production() trả về config mới với debug=False, workers=8

🧠 Quiz

Câu hỏi: Khi dùng replace() trên frozen dataclass chứa nested frozen dataclass, nested object có được copy deep không?

A. Có — replace() luôn deep copy mọi thứ B. Không — replace() chỉ shallow copy, nested object được chia sẻ C. Tùy thuộc vào __copy__ method D. Python raise lỗi khi replace() trên nested frozen dataclass

Đáp án: Breplace() tạo shallow copy. Nếu bạn cần thay đổi nested object, phải replace() nested object trước rồi truyền vào outer replace().

Lời giải tham khảo
python
from dataclasses import dataclass, replace


@dataclass(frozen=True)
class ServerConfig:
    host: str = "0.0.0.0"
    port: int = 8000
    workers: int = 4
    debug: bool = False

    def __post_init__(self):
        if not (1 <= self.port <= 65535):
            raise ValueError(f"Port không hợp lệ: {self.port}")
        if self.workers < 1:
            raise ValueError(f"Workers phải >= 1: {self.workers}")


@dataclass(frozen=True)
class AppConfig:
    name: str
    version: str
    server: ServerConfig

    def for_testing(self) -> "AppConfig":
        """Config dành cho môi trường test."""
        new_server = replace(self.server, debug=True, workers=1)
        return replace(self, server=new_server)

    def for_production(self) -> "AppConfig":
        """Config dành cho môi trường production."""
        new_server = replace(self.server, debug=False, workers=8)
        return replace(self, server=new_server)


# Sử dụng
base = AppConfig(
    name="order-api",
    version="3.0.1",
    server=ServerConfig(host="0.0.0.0", port=8000),
)

test_cfg = base.for_testing()
prod_cfg = base.for_production()

print(test_cfg.server.debug)    # True
print(test_cfg.server.workers)  # 1
print(prod_cfg.server.workers)  # 8

# base không bị thay đổi (immutable)
print(base.server.debug)   # False
print(base.server.workers)  # 4

Bài 3: So sánh hiệu năng thực tế

🧠 Quiz

Câu hỏi: Đoạn code nào tạo 100,000 instance nhanh nhất?

A.

python
@dataclass
class Point:
    x: float
    y: float

B.

python
@dataclass(slots=True)
class Point:
    x: float
    y: float

C.

python
@dataclass(frozen=True, slots=True)
class Point:
    x: float
    y: float

D. Cả ba gần như ngang nhau

Đáp án: Bslots=True nhanh nhất do bỏ overhead tạo __dict__. frozen=True thêm overhead kiểm tra immutability mỗi lần gán. Dataclass thường (A) chậm hơn B do tạo __dict__. Tuy nhiên sự khác biệt thường dưới 20% — chọn dựa trên tính đúng đắn trước, tối ưu sau.

Liên kết học tiếp