Menu
CoddyTech

公有、受保护和私有成员

本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 28 课,共 64 课。

Python 对类成员有三种访问控制级别:公开、受保护和私有。它们控制属性和方法的访问方式。

下面是一个包含全部三种访问级别的类示例:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # 公有:可在任何地方访问
        self._balance = balance      # 受保护:供内部使用
        self.__account_id = account_id  # 私有:仅限类内部使用
    
    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)        # 运行正常

访问受保护成员(单个下划线,仅为约定):

print(account._balance)     # 1500,可以运行,但不推荐这样做
result = account._calculate_interest()  # 可以运行,但不推荐这样做
print(result)               # 30.0

尝试访问私有成员(双下划线,名称改写):

# 这样不行:
# print(account.__account_id)  # AttributeError

# 但这样可以(名称改写):
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"
savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance())  # 2000
print(savings.show_id())       # 无法访问私有成员

输出:

Alice
30.0
12345
2000
Cannot access private member

重点:公共成员没有前缀,可以在任何地方访问。受保护成员使用单下划线(_),应该只在类层次结构中使用。私有成员使用双下划线(__),并会进行名称改写,以实现更强的隐私保护。Python 的访问控制基于约定,而不是严格强制执行。

challenge icon

挑战

中等

在这个挑战中,你将实现一个 BankAccount 类,演示如何在 Python 中正确使用 public、protected 和 private 访问级别。

  • bankaccount.py:包含类定义,其中的 TODO 注释会引导你完成实现
  1. 按照 bankaccount.py 中的 TODO 注释,实现所需功能
  2. 按照要求设置适当的访问级别(public、protected、private)
  3. 确保所有方法都能妥善处理边界情况(例如存款金额为负数)

亲手试试

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":
    # 演示私有属性的名称改写机制
    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自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Python 在线编译器