パブリック、プロテクテッド、プライベートメンバ
CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 28/64。
Pythonには、クラスメンバに対してpublic、protected、およびprivateの3つのアクセス制御レベルがあります。これらは、属性やメソッドがどのようにアクセスされるかを制御します。
以下は、3つのアクセスレベルすべてを備えたクラスの例です:
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) # Alice
account.deposit(500) # 正常に動作します保護されたメンバーへのアクセス (アンダースコア1つ - 慣習のみ):
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重要なポイント:パブリックメンバにはプレフィックスがなく、どこからでもアクセス可能です。プロテクテッドメンバはアンダースコア1つ(_)を使用し、クラス階層内でのみ使用されるべきです。プライベートメンバはアンダースコア2つ(__)を使用し、より強力なプライバシーのために名前修飾(ネームマングリング)が行われます。Pythonのアクセス制御は慣習に基づいており、厳密に強制されるものではありません。
チャレンジ
中級このチャレンジでは、Pythonにおけるパブリック、プロテクテッド、およびプライベートのアクセスレベルの適切な使用方法を示す BankAccount クラスを実装します。
bankaccount.py- 実装のガイドとなる TODO コメントを含むクラス定義が含まれています。
bankaccount.py内の TODO コメントに従って、必要な機能を実装してください。- 指定された通りに、適切なアクセスレベル(パブリック、プロテクテッド、プライベート)を実装してください。
- すべてのメソッドがエッジケース(負の預け入れなど)を適切に処理するようにしてください。
チートシート
Pythonには、クラスメンバに対して3つのアクセス制御レベルがあります:
- パブリック (Public): 接頭辞なし、どこからでもアクセス可能
- プロテクテッド (Protected): アンダースコア1つ (
_)、クラス階層内での内部利用 - プライベート (Private): アンダースコア2つ (
__)、クラス内のみのアクセスのために名前修飾される
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) # 公開属性
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}")
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。