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) # 12345protected와 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)이 적용됩니다. 파이썬의 접근 제어는 엄격하게 강제되는 것이 아니라 관례를 기반으로 합니다.
챌린지
중급이 챌린지에서는 Python에서 public, protected, private 접근 수준의 올바른 사용법을 보여주는 BankAccount 클래스를 구현합니다.
bankaccount.py- 구현을 안내하는 TODO 주석이 포함된 클래스 정의가 들어 있습니다.
bankaccount.py의 TODO 주석을 따라 필요한 기능을 구현하세요.- 지정된 대로 적절한 접근 수준(public, protected, private)을 구현하세요.
- 모든 메서드가 예외 상황(음수 입금 등)을 적절하게 처리하도록 하세요.
치트 시트
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}")
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.