Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Атрибуты, начинающиеся с __ (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__