Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What is Encapsulation?
What you need to know
Underscore conventions
| Name | Meaning | Enforced? |
|---|---|---|
balance | public | — |
_balance | internal; not part of the public API | no, just a convention |
__balance | name-mangled to _ClassName__balance | only renamed, still reachable |
__init__ | dunder: special method, not private at all | — |
1class Account:2 def __init__(self):3 self._balance = 1004 self.__pin = "1234"56acc = Account()7print(acc._balance) # 100 -> works; the underscore is a polite "please don't"8try:9 acc.__pin10except AttributeError as e:11 print("AttributeError") # AttributeError12print(acc._Account__pin) # 1234 -> mangled, not hiddenName mangling exists so that a subclass defining its own __pin does not overwrite the parent's by accident. It is not security.
@property: validated attributes
Java-style get_x()/set_x() methods are unusual in Python. Start with a plain attribute. When you later need validation, turn it into a property; callers keep writing obj.x, and their code does not change.
1class GenerationConfig:2 def __init__(self, temperature=0.2):3 self.temperature = temperature # goes through the setter below45 @property6 def temperature(self):7 return self._temperature89 @temperature.setter10 def temperature(self, value):11 if not 0.0 <= value <= 2.0:12 raise ValueError(f"temperature must be between 0 and 2, got {value}")13 self._temperature = value1415cfg = GenerationConfig()16cfg.temperature = 0.717print(cfg.temperature) # 0.718try:19 cfg.temperature = 720except ValueError as e:21 print(e) # temperature must be between 0 and 2, got 7Without a setter, the property is read-only. For immutable records, @dataclass(frozen=True) blocks all assignments after creation.
A real-life example
A shared GenerationConfig object is passed through an LLM pipeline. A teammate's script sets cfg.temperature = 7 (they meant 0.7). Without validation, the value travels through three services and the provider rejects it with an HTTP 400 in the middle of a batch of 20,000 requests, hours later. With the property above, the mistake raises ValueError on the exact line that made it, before any money is spent.
The same idea protects any invariant: a Budget whose spent can only grow through charge(), or a ChatSession whose history is exposed as a read-only tuple so no caller can delete messages behind its back.
Follow-up questions to expect
- "Does Python have private variables?" — Not enforced ones.
_xis a convention and__xis name-mangled; both are still reachable. Python relies on "we're all consenting adults". - "What is name mangling?" — Python renames
__xinside classCto_C__x, so subclasses cannot accidentally overwrite it. - "Why
@propertyinstead ofget_x()?" — It keeps the simpleobj.xsyntax and lets you add validation later without changing any caller.