公有、受保护和私有成员
本课属于 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 的访问控制基于约定,而不是严格强制执行。
挑战
中等在这个挑战中,你将实现一个 BankAccount 类,演示如何在 Python 中正确使用 public、protected 和 private 访问级别。
bankaccount.py:包含类定义,其中的 TODO 注释会引导你完成实现
- 按照
bankaccount.py中的 TODO 注释,实现所需功能 - 按照要求设置适当的访问级别(public、protected、private)
- 确保所有方法都能妥善处理边界情况(例如存款金额为负数)
亲手试试
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}")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Python 在线编译器