Exercise 2: Adding a 'viewer' Role for check_balance() — Possible Solution ==================================================================== THE CHANGES, ON BOTH CLASSES ------------------------------ class BankAccount: def __init__(self, balance): self.balance = balance def withdraw(self, amount): self.balance -= amount return self.balance def check_balance(self): # new, on the real object return self.balance class BankAccountProxy: def __init__(self, account, user_role): self._account = account self._user_role = user_role def withdraw(self, amount): if self._user_role != 'owner': raise PermissionError('Only the account owner can withdraw') return self._account.withdraw(amount) def check_balance(self): # new, on the proxy if self._user_role not in ('owner', 'viewer'): raise PermissionError('Not authorized to check balance') return self._account.check_balance() check_balance() on the proxy uses its own, DELIBERATELY DIFFERENT permission rule from withdraw() - it allows both 'owner' and 'viewer', while withdraw() still only allows 'owner'. Each method decides its own access rule independently rather than sharing one blanket check. VERIFYING A VIEWER CAN CHECK BUT NOT WITHDRAW ------------------------------ Wrapping a BankAccount(500) in a BankAccountProxy(account, 'viewer'): viewer check_balance(): 500 viewer withdraw correctly blocked: Only the account owner can withdraw balance unchanged after blocked attempt: 500 The viewer-role proxy successfully returns the balance, but the exact same PermissionError from this chapter's own withdraw() check still fires when the viewer tries to withdraw - and the real account's balance is confirmed unchanged afterward, exactly like the guest-role example this chapter already verified. WHY THIS IS STILL GENUINELY ONE PROXY, NOT TWO ------------------------------ A single BankAccountProxy object now enforces two independent, per-method access rules over the one real object it wraps - this is the natural extension of a protection proxy as an interface grows: as BankAccount gains more methods, the proxy gains a matching method with its own access check, all while the real BankAccount class itself never needs any awareness that a proxy exists in front of it. WHY THIS WORKS AS AN ANSWER ------------------------------ The new method is added in parallel on both the real object and the proxy, following this chapter's own established pattern, its own access rule is deliberately distinct from withdraw()'s rule rather than reusing it blindly, and both the allowed and blocked outcomes are verified together with the real account's balance checked as unchanged after the blocked attempt.