Menu
Coddy logo textTech

Public, Protected, Private 멤버

Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 28번째.

Python은 클래스 멤버에 대해 세 가지 수준의 액세스 제어(public, protected, private)를 제공합니다. 이들은 속성과 메서드에 접근하는 방식을 제어합니다.

다음은 세 가지 접근 수준을 모두 포함하는 클래스의 예입니다:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Public - 어디서나 접근 가능
        self._balance = balance      # Protected - 내부용
        self.__account_id = account_id  # Private - 클래스 내부에서만 접근 가능
    
    def deposit(self, amount):       # Public 메서드
        self._balance += amount
    
    def _calculate_interest(self):   # Protected 메서드
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Private 메서드
        return amount > 0 and amount <= self._balance

어디에서나 public 멤버에 접근할 수 있습니다:

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        # Protected - 서브클래스에서 접근 가능
    
    def show_id(self):
        # return self.__account_id  # 작동하지 않음 - private
        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)이 적용됩니다. 파이썬의 접근 제어는 엄격하게 강제되는 것이 아니라 관례를 기반으로 합니다.

challenge icon

챌린지

중급

이 챌린지에서는 Python에서 public, protected, private 접근 수준의 올바른 사용법을 보여주는 BankAccount 클래스를 구현합니다.

  • bankaccount.py - 구현을 안내하는 TODO 주석이 포함된 클래스 정의가 들어 있습니다.
  1. bankaccount.py의 TODO 주석을 따라 필요한 기능을 구현하세요.
  2. 지정된 대로 적절한 접근 수준(public, protected, private)을 구현하세요.
  3. 모든 메서드가 예외 상황(음수 입금 등)을 적절하게 처리하도록 하세요.

치트 시트

Python은 클래스 멤버에 대해 세 가지 접근 제어 수준을 가집니다:

  • Public: 접두사가 없으며, 어디서나 접근 가능
  • Protected: 단일 밑줄(_), 클래스 계층 구조 내에서 내부적으로 사용
  • Private: 이중 밑줄(__), 클래스 내부에서만 접근 가능하도록 이름 장식(name-mangling)됨
class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Public
        self._balance = balance      # Protected
        self.__account_id = account_id  # Private
    
    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)  # 공개(Public) 속성
    print(account.deposit(100))    # 공개(Public) 메서드
    print(account.get_balance())   # 공개(Public) 메서드

elif test_case == "transaction_count":
    # 보호된(Protected) 속성 테스트
    account = BankAccount("Jane Smith", "67890")
    account.deposit(50)
    account.deposit(100)
    account.deposit(200)
    print(account._transaction_count)  # 보호된(Protected) 속성
    print(account.get_balance())

elif test_case == "invalid_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":
    # 비공개(Private) 속성 접근 테스트
    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의 모든 레슨