← Назад к вопросам

Как называется атрибут начинающиеся с __?

1.2 Junior🔥 71 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Атрибуты, начинающиеся с __ (double underscore)

Атрибуты и методы, начинающиеся с двойного подчёркивания (__), называются приватными или name-mangled атрибутами. Это одна из ключевых особенностей инкапсуляции в Python.

1. Name Mangling — механизм скрытия

Когда атрибут начинается с __, Python автоматически переименовывает его (mangling) во время компиляции, добавляя префикс с именем класса:

class BankAccount:
    def __init__(self, balance: float):
        self.__balance = balance  # Приватный атрибут
    
    def deposit(self, amount: float):
        self.__balance += amount
    
    def get_balance(self):
        return self.__balance

# Использование
account = BankAccount(1000)
print(account.get_balance())  # 1000

# Прямое обращение к __balance невозможно (будет ошибка)
# print(account.__balance)  # AttributeError

# Но Python переименовал его на _BankAccount__balance
print(account._BankAccount__balance)  # 1000 (это работает, но не делай так!)

Ключевая идея: __balance → переименуется в _BankAccount__balance для предотвращения случайного доступа из подклассов и для скрытия реализации.

2. Защита от перезаписи в подклассах

Основная цель name-mangling — предотвратить конфликты имён в иерархии наследования:

class Parent:
    def __init__(self):
        self.__private = "родитель"
    
    def show(self):
        print(f"Parent.__private = {self.__private}")

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.__private = "ребёнок"  # Это НЕ перезаписывает Parent.__private!
    
    def show(self):
        print(f"Child.__private = {self.__private}")
        # Обращение к родительскому приватному атрибуту
        print(f"Parent.__private = {self._Parent__private}")

child = Child()
child.show()
# Child.__private = ребёнок
# Parent.__private = родитель

# Атрибуты разные!
print(dir(child))  # Увидишь _Parent__private и _Child__private

Результат: Каждый класс имеет свой приватный атрибут, нет конфликтов!

3. Различие между __, _, и обычными атрибутами

class Example:
    public_attr = "публичный"          # Обычный атрибут (можно менять)
    _protected_attr = "защищённый"    # Single underscore (convention)
    __private_attr = "приватный"      # Double underscore (name mangling)
    
    def public_method(self):
        return "публичный метод"
    
    def _protected_method(self):
        return "защищённый метод"
    
    def __private_method(self):
        return "приватный метод"

obj = Example()

# Публичный — доступен всегда
print(obj.public_attr)      # публичный
obj.public_attr = "новый"   # Разрешено менять

# Защищённый — по соглашению, но доступен
print(obj._protected_attr)  # защищённый (не трогай, но можно)

# Приватный — переименован
print(obj.__private_attr)   # AttributeError! Нет такого атрибута
print(obj._Example__private_attr)  # приватный (работает, но не делай так)

Таблица различий:

ТипПрефиксВидимостьИспользование
ПубличныйнетвездеAPI, можно менять
Защищённый_в классе, подклассахвнутренняя логика, соглашение
Приватный__только в классеполная скрытость реализации

4. Двойное подчёркивание в конце (__attr__)

Это магические методы (dunder methods) — специальные методы Python:

class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y
    
    def __str__(self) -> str:
        """Строковое представление для print()"""
        return f"Point({self.x}, {self.y})"
    
    def __repr__(self) -> str:
        """Представление для отладки"""
        return f"Point(x={self.x}, y={self.y})"
    
    def __add__(self, other: "Point") -> "Point":
        """Перегрузка оператора +"""
        return Point(self.x + other.x, self.y + other.y)
    
    def __eq__(self, other: "Point") -> bool:
        """Перегрузка оператора =="""
        return self.x == other.x and self.y == other.y
    
    def __len__(self) -> int:
        """Позволяет вызывать len()"""
        return 2

# Использование
p1 = Point(1, 2)
p2 = Point(3, 4)

print(str(p1))          # Point(1, 2)
print(repr(p1))         # Point(x=1, y=2)
print(p1 + p2)          # Point(4, 6)
print(p1 == p2)         # False
print(len(p1))          # 2

Важно: Магические методы (__method__) — это НЕ приватные! Они используются для переопределения встроенного поведения.

5. Практический пример: инкапсуляция

class User:
    def __init__(self, name: str, age: int):
        self.__name = name
        self.__age = age
    
    # Геттер для имени
    @property
    def name(self) -> str:
        return self.__name
    
    # Сеттер с валидацией
    @name.setter
    def name(self, value: str) -> None:
        if not value or len(value) < 2:
            raise ValueError("Имя должно быть минимум 2 символа")
        self.__name = value
    
    # Геттер для возраста
    @property
    def age(self) -> int:
        return self.__age
    
    # Сеттер с валидацией
    @age.setter
    def age(self, value: int) -> None:
        if value < 0 or value > 150:
            raise ValueError("Возраст должен быть от 0 до 150")
        self.__age = value

# Использование
user = User("Иван", 30)
print(user.name)  # Иван
print(user.age)   # 30

# Изменение с валидацией
user.name = "Петр"
user.age = 35

# Попытка установить неправильное значение
try:
    user.age = 200  # Ошибка!
except ValueError as e:
    print(f"Ошибка: {e}")

6. Name Mangling при наследовании

class Logger:
    def __init__(self):
        self.__level = "INFO"
    
    def log(self):
        return f"[{self.__level}] Логирование"

class AdvancedLogger(Logger):
    def __init__(self):
        super().__init__()
        self.__level = "DEBUG"  # Свой приватный атрибут
    
    def log(self):
        return f"Продвинутое: [self.__level]" + super().log()

logger = AdvancedLogger()
print(logger.log())
# Продвинутое: [DEBUG][INFO] Логирование

7. Когда НЕ использовать __

# ❌ Неправильно — переусложнение
class BadExample:
    def __init__(self):
        self.__private_counter = 0  # Зачем приватный счётчик?
    
    def __increment(self):  # И приватный метод?
        self.__private_counter += 1

# ✅ Правильно — используй _ для условной приватности
class GoodExample:
    def __init__(self):
        self._counter = 0  # Понятно, что это "внутри"
    
    def _increment(self):
        self._counter += 1

Правило: Используй __ только если реально нужна защита от подклассов. Для обычной инкапсуляции достаточно _.

8. Проверка name-mangled атрибутов

class Secret:
    def __init__(self):
        self.__password = "secret123"

obj = Secret()

# Всё переменные объекта
print(vars(obj))  # {"_Secret__password": "secret123"}

# Все атрибуты и методы
print(dir(obj))  # Есть _Secret__password, но нет __password

Резюме

Атрибуты с __:

  • Называются приватными или name-mangled
  • Автоматически переименуются в _ClassName__attrname
  • Защищают от конфликтов имён в наследовании
  • Не являются полной защитой — это просто соглашение и препятствие
  • Используй _ для обычной приватности (по соглашению)
  • Используй __ только если действительно необходимо

Магические методы (__method__):

  • Это НЕ приватные атрибуты
  • Используются для переопределения встроенного поведения Python
  • Примеры: __init__, __str__, __add__, __len__