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

Что из себя представляет метод с _ и __?

1.0 Junior🔥 181 комментариев
#DevOps и инфраструктура#Django

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

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

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

Методы с нижними подчёркиваниями в Python

Этот вопрос касается соглашений об именовании для обозначения видимости и специального поведения методов и атрибутов. В Python нет настоящих приватных переменных как в Java или C++, вместо этого используются соглашения, которые сигнализируют разработчику об интенциях.

Одиночное подчёркивание (_)

Одиночный underscore перед именем означает "внутреннее", но не строго приватное.

class User:
    def __init__(self, name: str):
        self._name = name  # Protected (по соглашению)
    
    def _validate_name(self) -> bool:
        """Внутренний метод для валидации. Использовать не рекомендуется"""
        return len(self._name) > 0
    
    def get_name(self) -> str:
        """Публичный метод для получения имени"""
        if self._validate_name():
            return self._name
        raise ValueError("Invalid name")

# Использование
user = User("John")
print(user.get_name())  # ✅ OK
print(user._name)       # ⚠️ Технически работает, но плохая практика
user._validate_name()   # ⚠️ Работает, но говорит "не трогай"

Что означает одиночное подчёркивание:

  • "Это внутренний атрибут"
  • "Используй публичные методы вместо этого"
  • Не защита, а соглашение между разработчиками
  • IDE и lint-инструменты могут предупредить об использовании

Практический пример:

class Database:
    def __init__(self, connection_string: str):
        self._connection = self._create_connection(connection_string)
        self._pool = self._create_pool()
    
    def _create_connection(self, conn_str: str):
        """Внутренний метод. Клиент должен использовать .connect()"""
        return psycopg2.connect(conn_str)
    
    def _create_pool(self):
        """Создать пул соединений. Внутренний метод"""
        return ConnectionPool(self._connection)
    
    def query(self, sql: str) -> list:
        """Публичный метод для выполнения запроса"""
        # Использует _pool и _connection внутри
        return self._connection.execute(sql).fetchall()

Двойное подчёркивание (__)

Двойное подчёркивание (манглинг имён) — это механизм для сокрытия данных.

Когда вы используете __attribute, Python применяет name mangling — преобразует имя в _ClassName__attribute.

class BankAccount:
    def __init__(self, balance: float):
        self.__balance = balance  # Приватный атрибут
    
    def deposit(self, amount: float) -> None:
        """Публичный метод для пополнения"""
        if amount > 0:
            self.__balance += amount
        else:
            raise ValueError("Amount must be positive")
    
    def withdraw(self, amount: float) -> None:
        """Публичный метод для снятия"""
        if 0 < amount <= self.__balance:
            self.__balance -= amount
        else:
            raise ValueError("Insufficient funds")
    
    def get_balance(self) -> float:
        """Публичный метод для получения баланса"""
        return self.__balance

# Использование
account = BankAccount(1000)
account.deposit(500)  # ✅ OK
print(account.get_balance())  # ✅ OK: 1500

# Попытка прямого доступа
print(account.__balance)  # ❌ AttributeError: 'BankAccount' object has no attribute '__balance'

# Но name mangling позволяет обойти защиту (не рекомендуется!)
print(account._BankAccount__balance)  # ⚠️ 1500 (работает, но это нарушение инкапсуляции)

Как работает name mangling:

class Example:
    def __init__(self):
        self.__private = "secret"
        self._protected = "internal"
        self.public = "visible"

obj = Example()

# Проверить атрибуты
print(dir(obj))
# [..._Example__private, _protected, public, ...]

# Доступ
print(obj.public)                    # ✅ visible
print(obj._protected)                # ✅ internal (по соглашению)
print(obj.__private)                 # ❌ AttributeError
print(obj._Example__private)         # ⚠️ secret (обход защиты)

Двойное подчёркивание ДО и ПОСЛЕ (name)

Специальные методы (dunder methods) — это методы с __ с обеих сторон.

Это не показатель видимости, а магические методы, которые Python вызывает в особых ситуациях.

class Person:
    def __init__(self, name: str):
        """Конструктор — вызывается при создании объекта"""
        self.name = name
    
    def __str__(self) -> str:
        """Преобразование в строку для print()"""
        return f"Person: {self.name}"
    
    def __repr__(self) -> str:
        """Представление объекта для отладки"""
        return f"Person(name='{self.name}')"
    
    def __len__(self) -> int:
        """Поддерживает len()"""
        return len(self.name)
    
    def __eq__(self, other) -> bool:
        """Поддерживает =="""
        if isinstance(other, Person):
            return self.name == other.name
        return False
    
    def __lt__(self, other) -> bool:
        """Поддерживает <"""
        return self.name < other.name
    
    def __add__(self, other):
        """Поддерживает +"""
        if isinstance(other, Person):
            return Person(self.name + " & " + other.name)
        return NotImplemented
    
    def __call__(self, *args, **kwargs):
        """Позволяет вызвать объект как функцию"""
        print(f"{self.name} called with {args} {kwargs}")

# Использование
person = Person("Alice")

print(person)           # __str__: Person: Alice
print(repr(person))     # __repr__: Person(name='Alice')
print(len(person))      # __len__: 5

bob = Person("Bob")
print(alice == bob)     # __eq__: False
print(alice < bob)      # __lt__: False (A < B)

alice_bob = alice + bob  # __add__: Person(name='Alice & Bob')
person("arg1", key="value")  # __call__

Другие важные dunder методы:

class Container:
    def __init__(self, items: list):
        self._items = items
    
    def __getitem__(self, index) -> any:
        """Поддерживает container[index]"""
        return self._items[index]
    
    def __setitem__(self, index, value) -> None:
        """Поддерживает container[index] = value"""
        self._items[index] = value
    
    def __delitem__(self, index) -> None:
        """Поддерживает del container[index]"""
        del self._items[index]
    
    def __iter__(self):
        """Поддерживает for item in container"""
        return iter(self._items)
    
    def __contains__(self, item) -> bool:
        """Поддерживает item in container"""
        return item in self._items
    
    def __enter__(self):
        """Context manager: with statement"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager cleanup"""
        self._items.clear()

# Использование
container = Container([1, 2, 3])
print(container[0])        # __getitem__: 1
container[0] = 10          # __setitem__
print(2 in container)      # __contains__: True

for item in container:     # __iter__
    print(item)

with container as c:       # __enter__, __exit__
    print(c[0])

Сравнение соглашений

class Example:
    def public_method(self):
        """Публичный метод. Можно использовать везде"""
        pass
    
    def _protected_method(self):
        """Защищённый метод (по соглашению).
        Рекомендуется использовать только в подклассах
        """
        pass
    
    def __private_method(self):
        """Приватный метод (name mangling).
        Не должен использоваться извне класса
        """
        pass
    
    def __init__(self):
        """Специальный метод (dunder).
        Вызывается автоматически при создании объекта
        """
        pass

# Таблица
convention = {
    "public_method": "Публичный API. Стабильный, может использоваться везде",
    "_protected": "Внутренний. По соглашению — для подклассов и опытных пользователей",
    "__private": "Приватный. Name mangling. Сложно обойти, но возможно",
    "__magic__": "Специальный. Вызывается Python автоматически",
}

Заключение

Итоговая таблица:

СтильЗначениеИспользование
publicПубличный APIВезде
_internalВнутреннееПодклассы, опытные пользователи
__privateПриватное (name mangling)Только внутри класса
__magic__Специальное (dunder)Переопределение поведения

Важно понимать: Python не имеет настоящих приватных переменных. Всё это — соглашения между разработчиками. Профессиональный разработчик уважает эти соглашения и не пытается обойти их через _ClassName__private.

Что из себя представляет метод с _ и __? | PrepBro