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

Как использовать приватную функцию?

2.0 Middle🔥 111 комментариев
#Python Core

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

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

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

Приватные функции в Python

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

Способ 1: Одно подчёркивание (_) — "protected"

Функция с префиксом _условно приватная, это сигнал для других разработчиков: "Используй на свой риск".

def _internal_helper(x, y):
    """Вспомогательная функция, не для публичного API."""
    return x + y

def public_function(a, b):
    # Внутри модуля используем приватную функцию
    return _internal_helper(a, b) * 2

# Снаружи можно вызвать, но это нарушает договоренность
from mymodule import _internal_helper
result = _internal_helper(1, 2)  # Работает, но плохо!

Правило: _function используется внутри модуля.

Способ 2: Двойное подчёркивание (__) — name mangling

Функция с префиксом __ получает автоматическое переименование (name mangling), что затрудняет случайный доступ извне.

class Database:
    def __init__(self):
        self.connection = None
    
    def __connect(self):
        """Приватный метод для установки соединения."""
        self.connection = "Connected"
        return self.connection
    
    def query(self, sql):
        """Публичный метод."""
        if not self.connection:
            self.__connect()  # Используем приватный метод внутри
        return f"Executing: {sql}"

# Создаём экземпляр
db = Database()

# Публичный метод работает
print(db.query("SELECT * FROM users"))

# Приватный метод — НЕ работает
db.__connect()  # AttributeError!

# Но Python переименовал его, и он ВСЕ ЕЩЕ доступен
db._Database__connect()  # Работает! (плохая практика)

Как это работает:

# Python автоматически переименовает __method в _ClassName__method
class MyClass:
    def __private(self):
        pass

# Доступные методы:
print(dir(MyClass))
# [..., '_MyClass__private', ...]

Способ 3: Приватные функции в модуле

# utils.py

def _validate_email(email):
    """Приватная вспомогательная функция."""
    return "@" in email

def send_email(to_addr, subject, body):
    """Публичная функция."""
    if not _validate_email(to_addr):
        raise ValueError("Invalid email")
    # ... отправляем письмо
    return True

def _log_email_sent(email):
    """Приватная логирование."""
    print(f"Email sent to {email}")

Использование снаружи:

from utils import send_email

# ✅ Правильно: используем публичную функцию
send_email("user@example.com", "Hello", "World")

# ❌ Плохо: используем приватную функцию
from utils import _validate_email
email = _validate_email("test@example.com")  # Работает, но нарушает контракт

Способ 4: Использование __all__

Экспортируйте только публичные функции через __all__.

# math_utils.py

def _gcd(a, b):
    """Приватная функция — НОД."""
    while b:
        a, b = b, a % b
    return a

def lcm(a, b):
    """Публичная функция — НОК."""
    return abs(a * b) // _gcd(a, b)

def factorial(n):
    """Публичная функция."""
    if n <= 1:
        return 1
    return n * factorial(n - 1)

# Определяем публичный API
__all__ = ['lcm', 'factorial']

# При from math_utils import * — импортируются только функции из __all__

Использование:

# Импорт всех публичных функций
from math_utils import *

lcm(12, 18)  # ✅ Работает (в __all__)
factorial(5)  # ✅ Работает (в __all__)
_gcd(12, 18)  # ❌ NameError (не в __all__)

# Но явный импорт работает
from math_utils import _gcd
_gcd(12, 18)  # ✅ Работает (но плохо!)

Практический пример: класс с приватными методами

class BankAccount:
    def __init__(self, account_number, balance):
        self.account_number = account_number
        self.__balance = balance  # Приватный атрибут
    
    def __validate_transaction(self, amount):
        """Приватный метод для валидации."""
        if amount <= 0:
            raise ValueError("Amount must be positive")
        if amount > self.__balance:
            raise ValueError("Insufficient funds")
    
    def __record_transaction(self, amount, transaction_type):
        """Приватный метод для логирования."""
        print(f"Transaction: {transaction_type} {amount}")
    
    def deposit(self, amount):
        """Публичный метод для пополнения."""
        self.__validate_transaction(amount)
        self.__balance += amount
        self.__record_transaction(amount, "DEPOSIT")
        return self.__balance
    
    def withdraw(self, amount):
        """Публичный метод для снятия."""
        self.__validate_transaction(amount)
        self.__balance -= amount
        self.__record_transaction(amount, "WITHDRAW")
        return self.__balance
    
    def get_balance(self):
        """Публичный метод для получения баланса."""
        return self.__balance

# Использование
account = BankAccount("1234567890", 1000)
print(account.get_balance())  # 1000
account.deposit(500)  # DEPOSIT 500
account.withdraw(200)  # WITHDRAW 200
print(account.get_balance())  # 1300

# Попытка прямого доступа к приватному методу
account.__validate_transaction(100)  # AttributeError!

# Name mangling не защищает (но затрудняет доступ)
account._BankAccount__balance = 999999  # Работает (очень плохо!)

Лучшие практики

✅ Используйте _function для модуль-уровня

# utils.py
def _helper():
    pass

def public_api():
    return _helper()

✅ Используйте __method в классах для защиты от переопределения

class Parent:
    def __private(self):
        return "parent"

class Child(Parent):
    def __private(self):  # Это НЕ переопределит родительский
        return "child"

p = Parent()
c = Child()
print(p._Parent__private())  # "parent"
print(c._Child__private())   # "child"

✅ Документируйте приватность

def _internal_function(x):
    """
    Вспомогательная функция.
    
    ВНИМАНИЕ: Это приватная функция!
    Не используйте в публичном API.
    """
    return x * 2

❌ НЕ используйте приватные функции извне

# ❌ Плохо
from mymodule import _private_function
result = _private_function(42)

# ✅ Хорошо: используйте публичный API
from mymodule import public_function
result = public_function(42)

Сравнение подходов

СтильЗащитаВидимостьОшибкиИспользование
functionНетПубличнаяНетПубличный API
_functionСоглашениеСкрытаНетВнутренний API
__methodName manglingСкрытаAttributeErrorМетоды класса
__var__НетМагические методыНетВстроенные

Золотое правило: Приватность в Python — это договоренность между разработчиками, а не абсолютное ограничение. Используйте _ и __ как сигналы для других, не как стену безопасности.

Как использовать приватную функцию? | PrepBro