Модуль 6 · Урок 22 із 58
Encapsulation: property, classmethod і API boundary
Encapsulation у Python — не спроба зробити дані магічно недоступними. Це домовленість про public API: які names підтримуються, де перевіряються invariants і які implementation details caller не повинен використовувати як contract.
Безпечна практика модуля 6
Пакет містить тільки synthetic records, validated value objects, composition, Protocol, deterministic summary і локальні tests. Жодних персональних чи production-даних, secrets, мережевих викликів або зовнішніх залежностей.
Один underscore означає non-public convention
class WorkItem:
def __init__(self, minutes: int) -> None:
self._minutes = self._validate_minutes(minutes)
@staticmethod
def _validate_minutes(value: int) -> int:
if value < 0:
raise ValueError("minutes must be non-negative")
return valueName _minutes технічно доступний, але сигналізує: implementation detail може змінитися. Python не має абсолютних private instance variables. Double underscore запускає name mangling для уникнення випадкового collision у subclasses, а не створює security boundary.
Property додає поведінку до attribute interface
class WorkItem:
def __init__(self, minutes: int) -> None:
self._minutes = self._validate_minutes(minutes)
@property
def minutes(self) -> int:
return self._minutes
@minutes.setter
def minutes(self, value: int) -> None:
self._minutes = self._validate_minutes(value)Caller читає item.minutes, а class зберігає validation і representation control. Property виправдана, коли attribute semantics природні. Не ховайте під property повільний network call або destructive operation: syntax виглядає як дешеве читання.
Read-only property захищає derived state від розсинхронізації
@property
def hours(self) -> float:
return self._minutes / 60Якщо hours обчислюється з minutes, зберігати обидва values небезпечно: один можуть оновити без другого. Property without setter робить derived value read-only через public interface. Це не криптографічний захист, але чіткий maintenance contract.
classmethod — alternative constructor для самого class
class WorkItem:
@classmethod
def from_record(cls, record: dict[str, object]) -> "WorkItem":
return cls(minutes=int(record["minutes"]))classmethod отримує cls, тому subclass може створити свій type через inherited constructor. Adapter має виконати format-specific parsing, а canonical __init__ — остаточну invariant validation. Не копіюйте правила у кілька constructors.
staticmethod не бачить ні instance, ні class
Static method — function у namespace class. Вона доречна лише коли operation концептуально близька до type, але не потребує self чи cls. Якщо helper використовується ширше або не належить domain concept, module-level function простіша для повторного використання й test.
@staticmethod
def _validate_minutes(value: int) -> int:
if isinstance(value, bool) or not isinstance(value, int):
raise TypeError("minutes must be int")
return valuePublic API має бути малим і стабільним
Caller повинен знати constructor, documented properties і domain methods, але не layout internal fields. Не повертайте mutable internal list напряму: caller зможе обійти validation. Поверніть tuple, iterator або copy відповідно до performance contract.
Definition of Done
- Public і non-public names відділені та документовані.
- Property не приховує дорогий I/O або несподівані side effects.
- Alternative constructor делегує canonical invariant checks.
- Derived state не дублюється у mutable fields.
- Internal mutable collections не витікають через public API.
Методичні джерела
Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію Python.
Закріпіть матеріал уроку
Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.