Инкапсуляция и приватные атрибуты
Инкапсуляция и приватные атрибуты
Мы умеем создавать классы с атрибутами и методами. Но что мешает кому-то написать account.balance = -1000 и обойти все проверки? Инкапсуляция — принцип сокрытия внутреннего состояния объекта и предоставления контролируемого доступа через методы.
Проблема открытых атрибутов
class BankAccount:
def __init__(self, balance):
self.balance = balance
account = BankAccount(1000)
account.balance = -999999 # никто не проверяет!
print(account.balance) # -999999 — некорректное состояние
В Python нет настоящей private-защиты как в Java или C++. Вместо этого — соглашения, которые сообщают другим разработчикам: «этот атрибут внутренний».
Соглашение: одно подчёркивание _
Одно подчёркивание в начале — сигнал «я внутренний, не трогай без необходимости»:
class Temperature:
def __init__(self, celsius):
self._celsius = celsius # "не трогай напрямую"
def get_celsius(self):
return self._celsius
def set_celsius(self, value):
if value < -273.15:
raise ValueError("Ниже абсолютного нуля")
self._celsius = value
t = Temperature(25)
print(t.get_celsius()) # 25
t.set_celsius(100)
# t._celsius = -500 # технически можно, но это нарушение соглашения
Одно подчёркивание — конвенция, не принуждение. Python не запрещает обращаться к _celsius, но это говорит: «используешь на свой страх и риск».
Name mangling: двойное подчёркивание __
Двойное подчёркивание (__attr) включает механизм name mangling: Python переименовывает атрибут в _ClassName__attr, затрудняя случайный доступ снаружи:
class Person:
def __init__(self, name, secret):
self.name = name
self.__secret = secret # name mangling
def reveal_secret(self):
return f"Секрет {self.name}: {self.__secret}"
p = Person("Алиса", "боюсь пауков")
print(p.reveal_secret()) # Секрет Алисы: боюсь пауков
# print(p.__secret) # AttributeError!
print(p._Person__secret) # "боюсь пауков" — технически доступно, но некрасиво
Двойное подчёркивание не создаёт строгой приватности — скорее защищает от случайных коллизий имён в иерархиях наследования.
Property: атрибут + контроль через декоратор
Более питоничный способ — декоратор @property. Он позволяет обращаться к атрибуту как к обычному, но выполнять при этом логику:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Радиус должен быть положительным")
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2
c = Circle(5)
print(c.radius) # 5 — вызывает getter
c.radius = 10 # вызывает setter с проверкой
print(c.area) # 314.159... — вычисляется на лету
# c.radius = -1 # ValueError: Радиус должен быть положительным
@property — «умный атрибут»: снаружи выглядит как поле, внутри — метод. Это Python-идиом для контроля доступа без раздражающих get_x() / set_x().
Когда использовать приватность
В Python принят более свободный подход, чем в Java. Руководствуйтесь здравым смыслом:
_attr— внутренний атрибут, нежелательный для прямого использования снаружи.__attr— атрибут, который не должен случайно переопределяться в подклассах.@property— когда нужна валидация или вычисляемое значение за атрибутом-интерфейсом.- Публичный атрибут (без подчёркивания) — часть публичного API; менять его смысл — ломать совместимость.
Итог
- Инкапсуляция — сокрытие внутреннего состояния и контроль доступа через методы.
_attr— соглашение «внутренний»; Python не запрещает доступ.__attr— name mangling; переименовывается в_ClassName__attr.@property— управляемый доступ с синтаксисом атрибута.- Python доверяет разработчику: нет строгой приватности, только соглашения.
Модуль 12 завершён. В следующем модуле применим всё, что изучили: напишем финальный проект — консольную телефонную книгу с классами и сохранением в JSON.