Menu
Coddy logo textTech

パブリック、プロテクテッド、プライベートメンバ

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)  # 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

重要なポイント:パブリックメンバにはプレフィックスがなく、どこからでもアクセス可能です。プロテクテッドメンバはアンダースコア1つ(_)を使用し、クラス階層内でのみ使用されるべきです。プライベートメンバはアンダースコア2つ(__)を使用し、より強力なプライバシーのために名前修飾(ネームマングリング)が行われます。Pythonのアクセス制御は慣習に基づいており、厳密に強制されるものではありません。

challenge icon

チャレンジ

中級

このチャレンジでは、Pythonにおけるパブリック、プロテクテッド、およびプライベートのアクセスレベルの適切な使用方法を示す BankAccount クラスを実装します。

  • bankaccount.py - 実装のガイドとなる TODO コメントを含むクラス定義が含まれています。
  1. bankaccount.py 内の TODO コメントに従って、必要な機能を実装してください。
  2. 指定された通りに、適切なアクセスレベル(パブリック、プロテクテッド、プライベート)を実装してください。
  3. すべてのメソッドがエッジケース(負の預け入れなど)を適切に処理するようにしてください。

チートシート

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}")
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン