Перейти к содержанию

252 уроков, 17 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 5 из 20 Средний 35 мин 120 XP

Инкапсуляция: _protected и __private

Одно подчёркивание — договорённость, два — искажение имени. Учимся прятать детали класса за публичным интерфейсом и понимать name mangling.

Редакция Питоники

Методы из прошлого урока открывают класс наружу, но у класса есть и внутренняя кухня: баланс, который нельзя менять напрямую, кэш, служебные флаги. Инкапсуляция — умение разделить публичный интерфейс (что можно всем) и детали реализации (что трогать нельзя). В Python она построена не на запретах, а на договорённостях — и это работает.

Одно подчёркивание: внутреннее по договорённости

Одна ведущая черта — _имя — сигнал коллегам: «внутренняя деталь, не трогай». Язык при этом ничего не запрещает: атрибут читается как обычный. Защита здесь социальная, и это не слабость: большинство ошибок случается не от злого умысла, а от случайного изменения. Имя _score честно говорит — работай с интерфейсом, а не с внутренностями.

публичное имя и внутренний счёт
class Player:
    def __init__(self, name):
        self.name = name
        self._score = 0

    def add_points(self, points):
        if points > 0:
            self._score += points

hero = Player("Алиса")
hero.add_points(15)
print(hero.name, hero._score)
Вывод
Алиса 15
ИмяСтатусКто использует
nameпубличноевсе — это часть интерфейса
_nameвнутреннее по договорённостикод самого класса и его наследников
__nameприватное, с искажением именитолько сам класс
__name__магическое имя языканикто — свои имена в таком виде не придумывают

Двойное подчёркивание и name mangling

приватный баланс и его настоящее имя
class Account:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def get_balance(self):
        return self.__balance

acc = Account("Анна", 1000)
acc.deposit(500)
print(acc.get_balance())
print(acc.__dict__)
Вывод
1500
{'owner': 'Анна', '_Account__balance': 1500}

Фокус называется name mangling: внутри тела класса любое имя вида __name автоматически переименовывается в _ИмяКласса_name. Поэтому acc.__balance снаружи не существует — AttributeError, а словарь __dict__ показывает переименованное значение. Это не приватность: кто знает механику — достанет значение. Цель другой природы: такое имя невозможно случайно перекрыть в наследнике и невозможно спутать с интерфейсом. Инкапсуляция в Python — не стена, а правила приличия: язык прячет имя, но не запирает дверь.

интерфейс вместо деталей
class VendingMachine:
    def __init__(self, coins):
        self.__coins = coins

    def buy(self, price):
        if self.__coins >= price:
            self.__coins -= price
            print("Товар выдан")
        else:
            print("Недостаточно монет")

machine = VendingMachine(100)
machine.buy(40)
machine.buy(80)
Вывод
Товар выдан
Недостаточно монет

Инкапсуляция в Python — это культура интерфейса: публичные методы и свойства наружу, _детали и __внутренности — внутрь. Дальше: @property — элегантная замена пары get_balance() и set_balance(); наследование — где _protected-атрибуты особенно коварны: потомок видит всю внутреннюю кухню родителя.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

class Safe:
    def __init__(self):
        self.__code = 111

safe = Safe()
print(safe._Safe__code)
class Counter:
    def __init__(self):
        self.__value = 0

    def bump(self):
        self.__value += 2

    def value(self):
        return self.__value

c = Counter()
c.bump()
c.bump()
print(c.value())
class Account:
    def __init__(self, balance):
        self.__balance = balance

    def get_balance(self):
        return self.__balance

acc = Account(500)
acc.__balance = 9999
print(acc.get_balance())
Проверь себя
0 / 6

1. Что означает атрибут с одним подчёркиванием _score?

2. Что произойдёт при обращении acc.__balance извне класса Account?

3. Во что превратится self.__balance внутри класса Account?

4. Зачем классу публичные методы вроде deposit() вместо прямого доступа к балансу?

5. Защищает ли __name от доступа извне?

6. Имена вида __name__ (два подчёркивания с обеих сторон) — это...

Карточки терминов
Запомнено: 0 / 6
Практика

Создайте класс PiggyBank (копилка): __init__ принимает начальную сумму и хранит её в приватном атрибуте __coins. Добавьте метод add(coin), принимающий только монеты 1, 2, 5, 10 (любую другую печать «Такой монеты нет»), и метод smash(), который возвращает накопленное и обнуляет копилку. Заведите копилку на 50, добавьте 10, попробуйте добавить 7, затем разбейте копилку и выведите итог.

practice.py
Вопросы и ответы по уроку

Что такое инкапсуляция в Python простыми словами?

Это разделение класса на публичный интерфейс (методы, которыми пользуются все) и внутренние детали (атрибуты, которые трогать нельзя). В Python она держится на соглашениях: _имя просит не трогать, __имя дополнительно искажает имя, а все проверки живут в публичных методах.

Чем __name отличается от _name?

_name — чистая договорённость: атрибут доступен как есть, черта лишь сигнал «внутреннее». __name внутри класса переименовывается в _Класс__name (name mangling): обращение к исходному имени снаружи даёт AttributeError. Второй механизм надёжнее защищает от случайного перекрытия имён, но тоже не является настоящей приватностью.

Как по-настоящему ограничить доступ к атрибуту?

Никак — в Python принята модель «мы все согласные взрослые». Практическое решение: хранить данные в _private или __private атрибуте и выпускать наружу методы или @property с проверками. Чужой код сможет обойти защиту, но корректному коду она не даст испортить объект.

Урок 6: @property

Почему в Python нет настоящего private, как в Java?

Философия языка: читаемость и доверие важнее запретов. Настоящая приватность мешает отладке, тестированию и наследованию, а name mangling оставили для единственной реальной проблемы — случайных коллизий имён при наследовании. От дисциплинированного разработчика в Python и не требуется решёток.

Понравился урок? Сошлитесь на него

«Инкапсуляция в Python — не стена, а правила приличия: язык прячет имя, но не запирает дверь.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.