Menu
Coddy logo textTech

Public, Protected, Private члены класса

Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 28 из 64.

В Python существует три уровня управления доступом к членам класса: public, protected и private. Они определяют, каким образом можно получить доступ к атрибутам и методам.

Вот пример класса со всеми тремя уровнями доступа:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Публичный - доступен отовсюду
        self._balance = balance      # Защищенный - для внутреннего использования
        self.__account_id = account_id  # Приватный - только для класса
    
    def deposit(self, amount):       # Публичный метод
        self._balance += amount
    
    def _calculate_interest(self):   # Защищенный метод
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Приватный метод
        return amount > 0 and amount <= self._balance

Доступ к публичным членам из любого места:

account = BankAccount("Alice", 1000, "12345")
print(account.owner)        # Alice
account.deposit(500)        # Работает нормально

Доступ к защищенным членам (одиночное подчеркивание — только соглашение):

print(account._balance)     # 1500 — работает, но не рекомендуется
result = account._calculate_interest()  # Работает, но не рекомендуется
print(result)               # 30.0

Попробуйте получить доступ к приватным членам (двойное подчеркивание — искажение имен):

# Это не сработает:
# print(account.__account_id)  # AttributeError

# Но это работает (искажение имен):
print(account._BankAccount__account_id)  # 12345

Создайте подкласс, чтобы продемонстрировать защищенный (protected) и приватный (private) доступ:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Защищенный - доступен в подклассе
    
    def show_id(self):
        # return self.__account_id  # Это не сработает - приватный
        return "Cannot access private member"
savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance())  # 2000
print(savings.show_id())       # Невозможно получить доступ к приватному члену

Вывод:

Alice
30.0
12345
2000
Cannot access private member

Ключевой момент: Публичные члены (public) не имеют префикса и доступны отовсюду. Защищенные члены (protected) используют одно нижнее подчеркивание (_) и должны использоваться только внутри иерархии классов. Приватные члены (private) используют двойное нижнее подчеркивание (__) и подвергаются искажению имен (name-mangling) для обеспечения более строгой конфиденциальности. Управление доступом в Python основано на соглашениях, а не на строгом принуждении.

challenge icon

Задание

Средне

В этом испытании вы реализуете класс BankAccount, который демонстрирует правильное использование уровней доступа public, protected и private в Python.

  • bankaccount.py — содержит определение класса с комментариями TODO, которые помогут вам в реализации
  1. Следуйте комментариям TODO в bankaccount.py, чтобы реализовать необходимую функциональность
  2. Реализуйте соответствующие уровни доступа (public, protected, private), как указано
  3. Убедитесь, что все методы правильно обрабатывают пограничные случаи (отрицательные депозиты и т. д.)

Шпаргалка

В Python существует три уровня управления доступом к членам класса:

  • Public: Без префикса, доступен отовсюду
  • Protected: Одиночное подчеркивание (_), для внутреннего использования в иерархии классов
  • Private: Двойное подчеркивание (__), искажение имен для доступа только внутри класса
class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Публичный
        self._balance = balance      # Защищенный
        self.__account_id = account_id  # Приватный
    
    def deposit(self, amount):       # Публичный метод
        self._balance += amount
    
    def _calculate_interest(self):   # Защищенный метод
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Приватный метод
        return amount > 0 and amount <= self._balance

Доступ к членам:

account = BankAccount("Alice", 1000, "12345")

# Публичный доступ
print(account.owner)        # Работает нормально

# Защищенный доступ (работает, но не рекомендуется)
print(account._balance)     # 1500

# Приватный доступ (искажение имен)
print(account._BankAccount__account_id)  # 12345

В подклассах:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Защищенный — доступен
    
    def show_id(self):
        # return self.__account_id  # Приватный — недоступен
        return "Cannot access private member"

Попробуйте сами

from bank_account import BankAccount

# Обработчик комплексных тестовых случаев
test_case = input()

if test_case == "basic_test":
    # Тест базовой функциональности
    account = BankAccount("John Doe", "12345")
    print(account.account_holder)  # Публичный атрибут
    print(account.deposit(100))    # Публичный метод
    print(account.get_balance())   # Публичный метод

elif test_case == "transaction_count":
    # Тестирование защищенного атрибута
    account = BankAccount("Jane Smith", "67890")
    account.deposit(50)
    account.deposit(100)
    account.deposit(200)
    print(account._transaction_count)  # Защищенный атрибут
    print(account.get_balance())

elif test_case == "invalid_deposit":
    # Тестирование валидации в методе deposit
    account = BankAccount("Bob Johnson", "54321")
    print(account.deposit(-50))  # Должен вернуть None
    print(account.deposit(0))    # Должен вернуть None
    print(account.deposit(75))   # Должен вернуть 75
    print(account.get_balance())

elif test_case == "private_access":
    # Тестирование доступа к приватному атрибуту
    account = BankAccount("Alice Brown", "98765")
    try:
        print(account.__balance)  # Это вызовет ошибку AttributeError
    except AttributeError:
        pass
    
    try:
        print(account.__account_number)  # Это вызовет ошибку AttributeError
    except AttributeError:
        pass
    
    print("Private attributes are not directly accessible")
    account.deposit(500)
    print(account.get_balance())  # Доступ через публичный метод

elif test_case == "name_mangling":
    # Демонстрация искажения имен (name mangling) для приватных атрибутов
    account = BankAccount("Charlie Green", "13579")
    print(account._BankAccount__balance)        # Доступ к приватному атрибуту через искажение имени
    print(account._BankAccount__account_number)  # Доступ к приватному атрибуту через искажение имени
    account.deposit(300)
    print(account._BankAccount__balance)  # Должен показать обновленный баланс

elif test_case == "multiple_accounts":
    # Тестирование нескольких аккаунтов
    account1 = BankAccount("David White", "24680")
    account2 = BankAccount("Eva Black", "86420")
    account3 = BankAccount("Frank Red", "97531")
    
    account1.deposit(150)
    account2.deposit(250)
    account2.deposit(50)
    account3.deposit(500)
    account3.deposit(300)
    account3.deposit(200)
    
    print(f"{account1.account_holder}: {account1.get_balance()}")
    print(f"{account2.account_holder}: {account2.get_balance()}")
    print(f"{account3.account_holder}: {account3.get_balance()}")
    
    print(f"Account 1 transactions: {account1._transaction_count}")
    print(f"Account 2 transactions: {account2._transaction_count}")
    print(f"Account 3 transactions: {account3._transaction_count}")

elif test_case == "stress_test":
    # Тест производительности с большим количеством транзакций
    account = BankAccount("Stress Tester", "11111")
    for _ in range(1000):
        account.deposit(1)
    print(f"Final balance: {account.get_balance()}")
    print(f"Transaction count: {account._transaction_count}")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming