- Python quản lý bộ nhớ như thế nào?
Python dùng hai cơ chế: (1) Reference counting — mỗi object đếm số tham chiếu, về 0 thì giải phóng ngay (2) Garbage Collector (module gc) — phát hiện và thu hồi circular references mà reference counting không xử lý được (3) Memory Pool (PyMalloc)…
- Mutable vs Immutable types trong Python?
Mutable types là kiểu dữ liệu có thể thay đổi giá trị tại chỗ sau khi tạo; immutable types là kiểu không thể thay đổi sau khi tạo, mọi thao tác "sửa" thực chất tạo ra object mới. Ví dụ: list, dict, set, bytearray là…
- `list`, `tuple`, `set`, `dict` — khi nào dùng cái nào?
Dùng list khi cần ordered collection có thể thay đổi. Dùng tuple cho dữ liệu bất biến, làm dict key, trả về nhiều giá trị từ function. Dùng set để loại bỏ duplicate và kiểm tra membership O(1). Dùng dict cho key-value lookup O(1). Lưu…
- Generator và `yield` là gì? Tại sao dùng?
Generator là function dùng yield trả về iterator — tính giá trị lazily, từng phần tử khi được yêu cầu. Tiết kiệm bộ nhớ so với list vì không nạp toàn bộ dữ liệu vào RAM. Dùng khi: xử lý file lớn, streaming data, infinite…
- Decorator là gì? Viết decorator đơn giản.
Decorator là higher-order function nhận một function và trả về function mới với hành vi bổ sung — cú pháp @decorator là syntactic sugar. Dùng @functools.wraps để giữ metadata của function gốc. Lưu ý: Không dùng @functools.wraps → mất name, doc của function gốc.
- Context Manager và `with` statement hoạt động thế nào?
Context manager quản lý tài nguyên tự động qua enter và exit. with đảm bảo cleanup chạy dù có exception hay không.
- Closure là gì? Khác với lambda thế nào?
Closure là nested function "ghi nhớ" biến từ enclosing scope dù outer function đã kết thúc. Lambda là anonymous function một dòng — thường dùng cho logic đơn giản tức thời. Lưu ý: Closure trong vòng lặp — mọi closure đều tham chiếu cùng biến…
- Shallow copy vs Deep copy — khác nhau thế nào?
Shallow copy tạo object mới nhưng elements bên trong vẫn tham chiếu cùng objects gốc — thay đổi mutable elements sẽ ảnh hưởng cả hai bản. Deep copy tạo hoàn toàn độc lập, copy đệ quy toàn bộ. Dùng copy.copy() cho shallow và copy.deepcopy() cho…
- Exception handling — `try/except/else/finally` hoạt động thế nào?
(1) try — khối code có thể raise exception (2) except — xử lý exception (3) else — chạy nếu KHÔNG có exception trong try (4) finally — LUÔN chạy, dù có exception hay không — dùng để cleanup.
- `is` vs `==` — khác nhau gì?
== so sánh giá trị (gọi eq). is so sánh identity — hai biến có trỏ đến cùng object trong memory không. Lưu ý: CPython cache integers nhỏ (-5 đến 256) và string interning — x = 256; y = 256; x is y có…
- `functools.lru_cache` hoạt động thế nào? Khi nào dùng?
lrucache (Least Recently Used cache) là decorator memoize kết quả của function — lần sau gọi cùng args sẽ trả về cache thay vì tính lại. Function phải có hashable arguments. Dùng cho: pure functions với expensive computation, recursive algorithms.
- f-string, `.format()`, `%` formatting — nên dùng cái nào?
Dùng f-string (Python 3.6+) — nhanh nhất, dễ đọc nhất, hỗ trợ expression trực tiếp. f"Name: {name!r}, Score: {score:.2f}". .format() khi cần template string tái sử dụng. % formatting là cũ, không nên dùng trong code mới. Ví dụ: f"2+2={2+2}" in ra "2+2=4" — expression…
- 4 nguyên lý OOP trong Python?
(1) Encapsulation — đóng gói data + methods, dùng (protected) và (name mangling) (2) Inheritance — class Child(Parent), hỗ trợ multiple inheritance, dùng super() (3) Polymorphism — cùng method tên, hành vi khác nhau tuỳ class; duck typing (4) Abstraction — ẩn implementation qua ABC…
- `@classmethod` vs `@staticmethod` vs instance method?
Instance method nhận self — có access vào instance và class. @classmethod nhận cls — access class, thường dùng làm factory. @staticmethod không nhận self hay cls — utility function liên quan đến class.
- MRO (Method Resolution Order) là gì? Python giải quyết diamond problem thế nào?
- `@property` decorator dùng thế nào?
@property cho phép define getter/setter/deleter cho attribute, truy cập như attribute thường nhưng có validation logic.
- Abstract class trong Python — dùng thế nào?
Dùng ABC + @abstractmethod để tạo abstract class. Class con bắt buộc implement tất cả abstract methods, nếu không sẽ không instantiate được.
- `@dataclass` là gì? Lợi ích so với class thường?
@dataclass tự động generate init, repr, eq — giảm boilerplate đáng kể. Ưu điểm so với namedtuple: có default values, methods, mutable (nếu không frozen).
- `__slots__` là gì? Khi nào nên dùng?
- Các dunder methods quan trọng nhất cần biết?
(1) repr — developer repr (eval-safe), str — user-friendly string (2) len, getitem, setitem — sequence protocol (3) enter/exit — context manager (4) eq, lt, hash — comparison và hashability (5) call — cho phép gọi instance như function (6) iter/next — iterator protocol. Lưu…
- GIL là gì? Ảnh hưởng đến threading và multiprocessing thế nào?
GIL (Global Interpreter Lock) là mutex trong CPython đảm bảo chỉ một thread thực thi Python bytecode tại một thời điểm. Ảnh hưởng: (1) I/O-bound tasks — threading vẫn hiệu quả vì GIL được release khi chờ I/O (network, file, DB) (2) CPU-bound tasks —…
- Threading vs Multiprocessing vs Asyncio — khi nào dùng cái nào?
Dùng asyncio cho I/O-bound với nhiều concurrent operations (web APIs, DB queries) — single thread, cooperative concurrency, overhead thấp nhất. Dùng threading cho I/O-bound với thư viện blocking không hỗ trợ async. Dùng multiprocessing cho CPU-bound (ML training, image processing, data transformation) — bypass GIL,…
- `async/await` và event loop hoạt động thế nào?
Event loop quản lý coroutines. Khi coroutine gặp await, nó tạm dừng và nhường control để event loop chạy coroutine khác. Đây là cooperative concurrency — không phải parallelism thực sự.
- `asyncio.gather` vs `asyncio.TaskGroup` — khác nhau gì?
- Race condition là gì? Cách phòng tránh trong asyncio?
- FastAPI là gì? Ưu điểm so với Flask và Django?
FastAPI là modern Python web framework dựa trên Starlette (ASGI) + Pydantic. Ưu điểm: (1) Async native — ~20K RPS, Flask/Django ~4-5K RPS (2) Auto OpenAPI docs — Swagger UI + ReDoc tự động từ type hints (3) Type-based validation — Pydantic validate request/response (4)…
- Pydantic validation trong FastAPI hoạt động thế nào?
FastAPI dùng Pydantic model để tự động validate request body, query params, path params. Trả về 422 Unprocessable Entity nếu invalid.
- Middleware trong FastAPI — viết custom middleware thế nào?
Middleware chạy trước và sau mỗi request. Dùng @app.middleware("http") cho custom middleware. CORS, GZip, TrustedHost — dùng built-in middlewares. Lưu ý: Middleware exception handler không bắt HTTP exceptions từ path operations — dùng exceptionhandler thay.
- JWT Authentication — implement đầy đủ trong FastAPI?
JWT auth gồm bốn bước: (1) Hash password với bcrypt (2) Tạo JWT token khi login (3) Verify token trong dependency (4) Protect routes với Depends(getcurrentuser). Dùng PyJWT (không phải python-jose — đã có CVEs và ít được maintain).
- Background Tasks vs Celery — khi nào dùng cái nào?
Background Tasks: zero config, in-process, chạy sau khi response trả về client. Dùng cho: email xác nhận đơn giản, logging, non-critical async work. Celery: cần Redis/RabbitMQ broker, distributed workers, retry logic, monitoring với Flower. Dùng cho: payment processing, report generation, critical tasks cần retry. Lưu…
- Lifespan events — startup/shutdown trong FastAPI 0.95+?
- N+1 Query Problem là gì? Cách giải quyết với SQLAlchemy?
N+1 problem: 1 query lấy N records, rồi N queries nữa để lấy related data. Giải quyết bằng eager loading.
- SQLAlchemy 2.0 async — setup cơ bản thế nào?
SQLAlchemy 2.0 async dùng createasyncengine, asyncsessionmaker, và ORM style mới với Mapped/mappedcolumn — fully type-safe.
- Testing FastAPI với pytest và TestClient?
Dùng TestClient cho synchronous-style test của async endpoints. Override dependencies để isolate tests.
- pytest fixtures — `scope` hoạt động thế nào?
Fixture scope xác định fixture được tạo và destroy khi nào: function (default) — mỗi test; class — mỗi test class; module — mỗi module; session — toàn bộ test session.
- Mocking với `pytest-mock` — cách dùng thế nào?
Dùng mocker fixture từ pytest-mock để mock functions và verify calls.
- Caching strategy trong Python API — Redis pattern?
- Pydantic Settings — cách quản lý config ứng dụng?
pydantic-settings tự động đọc env vars và validate types. Hỗ trợ .env file.
- Type hints — tại sao quan trọng trong Python hiện đại?
Type hints (PEP 484, Python 3.5+) không enforce lúc runtime nhưng mang lại: (1) IDE autocompletion và refactoring tốt hơn (2) Static analysis với mypy/pyright bắt lỗi trước runtime (3) Documentation tự động — code tự giải thích (4) Pydantic/FastAPI dùng type hints để validation…
- WebSocket trong FastAPI — implement thế nào?
- `*args` và `**kwargs` là gì? Khi nào dùng?
args cho phép nhận bất kỳ số positional arguments — nhóm thành tuple. kwargs nhận bất kỳ số keyword arguments — nhóm thành dict. Thứ tự params: def fn(pos, /, normal, args, kwonly, kwargs). Lưu ý: args và kwargs không giữ type information — dùng…
- `enumerate`, `zip`, `map`, `filter` — dùng khi nào?
Các built-ins quan trọng để xử lý collections hiệu quả: Lưu ý: map và filter trả về lazy iterators — cần list() để materialize. Modern Python ưu tiên list comprehension hơn map/filter vì dễ đọc hơn.
- Walrus operator `:=` (Assignment Expression) là gì? Khi nào hữu ích?
Walrus operator (PEP 572, Python 3.8+) cho phép assign và return giá trị trong cùng một expression — giảm lặp code. Lưu ý: không lạm dụng — walrus trong nested expression phức tạp làm code khó đọc hơn. Dùng khi thực sự tránh được duplicate…
- Type hints nâng cao: `Optional`, `Union`, `TypeVar`, `Generic`, `Protocol`?
Python type hints làm được nhiều hơn chỉ int, str cơ bản: Python 3.10+: dùng X Y thay vì Union[X, Y], X None thay vì Optional[X].
- Metaclasses trong Python là gì? Khi nào dùng?
- Generators vs Iterators — khác nhau như thế nào? `__iter__` và `__next__`?
Iterator: bất kỳ object nào implement iter() và next(). Generator: function dùng yield — tự động implement iterator protocol, lazy, stateful. Generator với send() — coroutine-lite, nhận value từ caller:
- Descriptor Protocol trong Python — `__get__`, `__set__`, `__delete__`?
- Async context managers và async generators — viết thế nào?
- `ThreadPoolExecutor` và `ProcessPoolExecutor` với asyncio?
- Pydantic v2 — những thay đổi lớn so với v1?
FastAPI 0.100+ dùng Pydantic v2 mặc định — nhanh hơn 5-50x nhờ Rust core. Thay đổi chính:
- Async vs sync endpoints trong FastAPI — khi nào dùng def hay async def?
FastAPI chạy trên Starlette (ASGI), hỗ trợ cả sync và async endpoints: async def: dùng khi endpoint thực hiện I/O async (database queries async, HTTP calls async): def (sync): khi dùng blocking operations không thể async. FastAPI tự động chạy sync endpoint trong ThreadPoolExecutor để…
- pytest parametrize — viết parameterized tests thế nào?
@pytest.mark.parametrize cho phép chạy cùng test với nhiều input/output khác nhau:
- Mock vs MagicMock vs AsyncMock — khác nhau gì? Khi nào dùng cái nào?
Mock là base class; MagicMock mở rộng với magic method support (len, iter, enter...); AsyncMock (Python 3.8+) bắt buộc cho coroutine — dùng sai type thì assertawaitedonce sẽ silently pass dù không được await.
- `@dataclass(slots=True)` — memory optimization trong Python 3.10+?
Kết hợp slots với @dataclass để có cả type safety và memory efficiency: Dùng @dataclass(slots=True) khi có hàng triệu instances — tiết kiệm ~30-60% memory.
- Sự khác biệt giữa `__new__` và `__init__` trong Python là gì?
- Python truyền tham số vào hàm theo cơ chế nào?
Python dùng cơ chế thường gọi là pass-by-object-reference (hay "call by assignment"): mỗi tham số được gán vào đúng object mà caller truyền vào — không copy object, cũng không phải pass-by-reference kiểu C++. Hệ quả tuỳ object là mutable hay không: - Mutable (list,…
- Quy tắc LEGB khi Python tra cứu tên là gì? `global` và `nonlocal` khác nhau ra sao?
LEGB là thứ tự Python tra một tên: Local (trong hàm hiện tại) → Enclosing (hàm bao ngoài) → Global (module) → Built-in. Tìm thấy ở tầng nào thì dừng ở đó. Mặc định, gán một tên bên trong hàm sẽ tạo biến Local —…
- List/dict/set comprehension là gì và khi nào nên dùng?
Cú pháp gọn để dựng collection mới bằng transform + filter trong một biểu thức: Dùng khi map/filter một iterable — ngắn và thường nhanh hơn vòng for + append. Không nên dùng khi logic nhiều nhánh, lồng sâu, hay có side-effect → vòng for…
- `if __name__ == "__main__":` dùng để làm gì?
Mỗi module có biến name. Khi bạn chạy file trực tiếp (python foo.py), name của file đó bằng "main". Khi file bị import từ nơi khác, name bằng tên module ("foo"). Đặt code chạy-thử / entry-point CLI trong khối này để nó chỉ chạy khi…
- `list.sort()` và `sorted()` khác nhau thế nào?
- list.sort(): sắp xếp tại chỗ, trả về None, chỉ dùng cho list. - sorted(iterable): trả về list mới, nhận mọi iterable (kể cả tuple, dict keys...). Cả hai đều nhận key= (hàm rút khoá so sánh) và reverse=. Cả hai dùng Timsort và ổn…
- Pickling/unpickling là gì? Vì sao unpickle dữ liệu không tin cậy lại nguy hiểm?
pickle serialize một object Python thành byte stream và deserialize ngược lại (dựng lại object). Khác JSON: giữ được cấu trúc Python phức tạp (object tuỳ biến, tham chiếu vòng) nhưng chỉ Python đọc được và không cross-language. Nguy hiểm: quá trình unpickle có thể…
- Exception Groups và `except*` (Python 3.11) giải quyết vấn đề gì?
- `match`-case (structural pattern matching, 3.10) khác `switch` truyền thống thế nào?
Không phải switch so-sánh-giá-trị đơn thuần. match so khớp cấu trúc: nó có thể destructure sequence/mapping/object, bắt biến từ bên trong, và thêm điều kiện if (guard). Trúng nhánh nào thì gán luôn các phần con vào biến. Khác biệt cần nhớ: - Không fall-through…
- `super()` thực sự làm gì? Cooperative multiple inheritance là gì?
- Single underscore và double underscore trong tên attribute khác nhau ra sao? Name mangling là gì?
Python không có access modifier thật (private/protected). Thay vào đó là quy ước và một cơ chế đổi tên: - name (1 gạch đầu): quy ước "nội bộ, đừng đụng từ ngoài" — thuần convention, không ràng buộc, vẫn truy cập được. - name (2…
- Vì sao hàm khai báo `def add(item, bucket=[])` lại giữ nguyên dữ liệu của những lần gọi trước?
Giá trị mặc định chỉ được tính đúng một lần, tại thời điểm câu lệnh def chạy — không phải mỗi lần gọi hàm. List đó được gắn vào chính object hàm (add.defaults), nên mọi lần gọi không truyền bucket đều dùng chung một list.…
- `grid = [[0] * 3] * 2` rồi `grid[0][0] = 1` cho kết quả gì? Vì sao?
Kết quả là [[1, 0, 0], [1, 0, 0]] — cả hai hàng cùng đổi. Toán tử trên list không sao chép phần tử, nó chỉ nhân bản tham chiếu: hai "hàng" thực chất là một object. Riêng [0] 3 vẫn đúng như mong đợi…
- `[x * x for x in data]` và `(x * x for x in data)` khác nhau thế nào? Khi nào nên chọn generator expression?
List comprehension dựng sẵn toàn bộ list trong RAM; generator expression trả về một lazy iterator, chỉ tính từng phần tử khi được duyệt. Khác biệt thực tế: - Bộ nhớ: genexp giữ một phần tử tại một thời điểm, hợp với file lớn, cursor…
- Vì sao `a = 256; b = 256; a is b` cho `True` nhưng thay bằng `257` lại `False`?
Vì CPython cấp phát sẵn và dùng chung các object số nguyên nhỏ trong khoảng -5 đến 256. Với giá trị nằm ngoài khoảng đó, mỗi lần tạo là một object mới nên is (so sánh identity) trả về False, dù == (so sánh giá…
- Viết decorator có tham số (vd `@retry(times=3)`) thế nào? `functools.wraps` giải quyết vấn đề gì?
Decorator có tham số cần ba tầng hàm: hàm ngoài nhận tham số cấu hình và trả về decorator, decorator nhận function và trả về wrapper. @retry(times=3) được đọc là: gọi retry(times=3) trước, lấy kết quả rồi mới áp lên hàm. functools.wraps để làm gì:…
- `yield from` khác gì so với viết `for item in sub: yield item`?
Về giá trị sinh ra thì giống nhau, nhưng yield from uỷ quyền hẳn cho sub-generator chứ không chỉ chuyển tiếp giá trị. Ba điểm khác biệt: 1. Chuyển tiếp hai chiều: send(), throw(), close() từ caller đi thẳng xuống sub-generator. Vòng for thủ công…
- Tự viết một context manager thế nào? Nếu `__exit__` trả về `True` thì điều gì xảy ra?
Hai cách: viết class có enter/exit, hoặc dùng @contextlib.contextmanager với một generator yield đúng một lần. Phần dọn dẹp phải nằm trong finally, nếu không thì block có exception sẽ không dọn. exit trả về True: exception bị nuốt hoàn toàn — chương trình chạy…
- Class định nghĩa `__eq__` nhưng không định nghĩa `__hash__` thì đưa instance vào `set` sẽ ra sao?
Ném TypeError: unhashable type. Python 3 tự đặt hash = None cho class nào override eq mà không khai báo hash. Lý do là ràng buộc giữa hash và equality: - a == b thì bắt buộc hash(a) == hash(b). - Hash phải không đổi…
- `@dataclass`, `NamedTuple` và `pydantic.BaseModel` — dự án thật nên dùng cái nào cho trường hợp nào?
Ba thứ giải quyết ba nhu cầu khác nhau, nhìn theo ranh giới dữ liệu: NamedTuple — bản ghi bất biến, nhẹ, có thể unpack và dùng làm key của dict. Hợp cho giá trị trả về nhiều thành phần, cấu hình chỉ đọc, toạ…
- Type hint có được Python kiểm tra lúc chạy không? `Optional[str]` nghĩa là gì?
Không. Runtime của Python bỏ qua annotation: def f(n: int) vẫn nhận "abc" và chạy bình thường, không có lỗi nào được ném ra. Type hint phục vụ công cụ phân tích tĩnh (mypy, pyright, IDE) và thư viện đọc metadata (pydantic, FastAPI, dataclasses, SQLAlchemy…
- Vì sao `fns = [lambda: i for i in range(3)]` gọi ra đều bằng 2?
Vì closure trong Python giữ tham chiếu tới biến, không giữ giá trị tại thời điểm tạo (late binding). Cả ba lambda cùng trỏ tới một biến i; đến lúc gọi thì vòng lặp đã kết thúc và i đang là 2. Ba cách sửa:…
- GIL đã bị gỡ chưa? Build free-threaded (PEP 703) thay đổi điều gì với code đa luồng?
- Một endpoint `async def` gọi hàm đồng bộ nặng (vd `requests.get`, resize ảnh) thì ảnh hưởng thế nào tới cả service? Phát hiện và xử lý ra sao?
- Reference counting đã giải phóng object ngay khi count về 0, vậy vì sao Python vẫn cần module `gc`? `weakref` dùng lúc nào?
- Khi nào Python ném `TypeError: Cannot create a consistent method resolution order (MRO)`?
- Cùng một script `multiprocessing`, chạy trên Linux thì ổn nhưng trên macOS/Windows lại lỗi hoặc chạy lặp vô hạn — nguyên nhân là gì?
- Một request cần gọi 200 API bên ngoài. Dùng `asyncio.gather` thẳng có vấn đề gì? Giới hạn đồng thời ra sao?
asyncio.gather khởi chạy tất cả coroutine cùng lúc. Với 200 lời gọi, phía bên kia dễ trả 429, kết nối bị nghẽn, và nếu vài request đồng thời cùng làm vậy thì tiến trình mở hàng nghìn socket. Cách chuẩn là giới hạn bằng asyncio.Semaphore…
- Đặt timeout cho lời gọi bên ngoài trong async Python: `asyncio.timeout` và `wait_for` khác gì? `CancelledError` xử lý ra sao?