Menu
Coddy logo textTech

Public, Protected, Private — pamięć

Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 28 z 64.

Python ma trzy poziomy kontroli dostępu do składowych klasy: publiczny, chroniony i prywatny. Określają one, jak można uzyskać dostęp do atrybutów i metod.

Oto przykład klasy ze wszystkimi trzema poziomami dostępu:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Publiczny - dostępny z dowolnego miejsca
        self._balance = balance      # Chroniony - do użytku wewnętrznego
        self.__account_id = account_id  # Prywatny - dostępny tylko w obrębie klasy
    
    def deposit(self, amount):       # Metoda publiczna
        self._balance += amount
    
    def _calculate_interest(self):   # Metoda chroniona
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Metoda prywatna
        return amount > 0 and amount <= self._balance

Dostęp do publicznych składowych z dowolnego miejsca:

account = BankAccount("Alice", 1000, "12345")
print(account.owner)        # Alice
account.deposit(500)        # Działa poprawnie

Dostęp do składowych chronionych (pojedyncze podkreślenie — tylko konwencja):

print(account._balance)     # 1500 - działa, ale nie jest zalecane
result = account._calculate_interest()  # Działa, ale nie jest zalecane
print(result)               # 30.0

Spróbuj uzyskać dostęp do prywatnych składowych (podwójne podkreślenie — zmiana nazwy):

# To nie zadziała:
# print(account.__account_id)  # AttributeError

# Ale to zadziała (modyfikowanie nazw):
print(account._BankAccount__account_id)  # 12345

Utwórz podklasę, aby pokazać różnicę między dostępem chronionym a prywatnym:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Chroniony - dostępny w klasie pochodnej
    
    def show_id(self):
        # return self.__account_id  # To nie zadziała - prywatny
        return "Cannot access private member"
savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance())  # 2000
print(savings.show_id())       # Nie można uzyskać dostępu do prywatnego elementu

Wynik:

Alice
30.0
12345
2000
Cannot access private member

Najważniejsza kwestia: składowe publiczne nie mają prefiksu i są dostępne wszędzie. Składowe chronione mają pojedyncze podkreślenie (_) i powinny być używane tylko w obrębie hierarchii klas. Składowe prywatne mają podwójne podkreślenie (__) i ich nazwy są modyfikowane, aby zapewnić większą prywatność. Kontrola dostępu w Pythonie opiera się na konwencji, a nie jest ściśle egzekwowana.

challenge icon

Wyzwanie

Średni

W tym wyzwaniu zaimplementujesz klasę BankAccount, która demonstruje prawidłowe użycie poziomów dostępu publicznego, chronionego i prywatnego w Pythonie.

  • bankaccount.py — zawiera definicję klasy z komentarzami TODO wskazującymi, co należy zaimplementować
  1. Postępuj zgodnie z komentarzami TODO w pliku bankaccount.py, aby zaimplementować wymaganą funkcjonalność
  2. Zaimplementuj odpowiednie poziomy dostępu (publiczny, chroniony, prywatny) zgodnie ze specyfikacją
  3. Upewnij się, że wszystkie metody prawidłowo obsługują sytuacje brzegowe (ujemne wpłaty itp.)

Spróbuj swoich sił

from bank_account import BankAccount

# Kompleksowa obsługa przypadków testowych
test_case = input()

if test_case == "basic_test":
    # Test podstawowej funkcjonalności
    account = BankAccount("John Doe", "12345")
    print(account.account_holder)  # Atrybut publiczny
    print(account.deposit(100))    # Metoda publiczna
    print(account.get_balance())   # Metoda publiczna

elif test_case == "transaction_count":
    # Testowanie atrybutu chronionego
    account = BankAccount("Jane Smith", "67890")
    account.deposit(50)
    account.deposit(100)
    account.deposit(200)
    print(account._transaction_count)  # Atrybut chroniony
    print(account.get_balance())

elif test_case == "invalid_deposit":
    # Testowanie walidacji w metodzie deposit
    account = BankAccount("Bob Johnson", "54321")
    print(account.deposit(-50))  # Powinno zwrócić None
    print(account.deposit(0))    # Powinno zwrócić None
    print(account.deposit(75))   # Powinno zwrócić 75
    print(account.get_balance())

elif test_case == "private_access":
    # Testowanie dostępu do atrybutu prywatnego
    account = BankAccount("Alice Brown", "98765")
    try:
        print(account.__balance)  # To spowoduje zgłoszenie wyjątku AttributeError
    except AttributeError:
        pass
    
    try:
        print(account.__account_number)  # To spowoduje zgłoszenie wyjątku AttributeError
    except AttributeError:
        pass
    
    print("Private attributes are not directly accessible")
    account.deposit(500)
    print(account.get_balance())  # Dostęp przez metodę publiczną

elif test_case == "name_mangling":
    # Demonstracja zmiany nazw atrybutów prywatnych
    account = BankAccount("Charlie Green", "13579")
    print(account._BankAccount__balance)        # Dostęp do atrybutu prywatnego przez zmienioną nazwę
    print(account._BankAccount__account_number)  # Dostęp do atrybutu prywatnego przez zmienioną nazwę
    account.deposit(300)
    print(account._BankAccount__balance)  # Powinno wyświetlić zaktualizowane saldo

elif test_case == "multiple_accounts":
    # Testowanie wielu kont
    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":
    # Test wydajności przy dużej liczbie transakcji
    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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online