工厂模式下@property重复调用致IndexError问题及修改方案咨询
问题分析
你的代码触发IndexError的核心原因是:AccountCreator.create_account方法中,allowed_types字典在定义时会立即实例化所有三种账户类型,而非仅实例化用户指定的类型。这导致每次调用create_account,都会消耗3个account_id和3个allowed_numbers元素——第一次调用就用掉了5个allowed_numbers中的3个,第二次调用时尝试再取3个,直接耗尽列表触发异常。
同时,这也是为什么你会观察到number()和account_id()各被调用4次(第一次调用create_account触发3次,第二次触发1次就报错)。
解决方案
修改AccountCreator的实现,让allowed_types存储账户类的引用,而非预先实例化的对象。当用户指定类型后,再动态实例化对应的账户类,确保每次create_account仅消耗1个account_id和1个allowed_numbers元素。
修改后的完整代码
from abc import ABC from random import choice, randint class AccountsManager: def __init__(self) -> None: self._last_id_number = 0 self._allowed_numbers = [randint(10_000, 99_999) for _ in range(5)] @property def number(self) -> int: if not self._allowed_numbers: raise IndexError number = choice(self._allowed_numbers) self._allowed_numbers.pop(self._allowed_numbers.index(number)) return number @property def account_id(self) -> int: account_id = self._last_id_number self._last_id_number += 1 return account_id class TemplateBankAccount(ABC): def __init__(self, manager, owner: str, account_type: str = '') -> None: self.manager = manager self.id_number = manager.account_id self.account_number = manager.number self.owner = owner self.account_type = account_type self._amount = 0 def __str__(self) -> str: raise NotImplementedError @property def amount(self) -> int: return self._amount @amount.setter def amount(self, direction: str, value: int) -> None: if direction == '+': self._amount += value elif direction == '-': self._amount -= value else: raise ValueError class PersonalBankAccount(TemplateBankAccount): def __init__(self, manager, owner) -> None: super().__init__(manager, owner, account_type='Personal Account') def __str__(self) -> str: return f'{self.account_type}: {self.owner}' class CompanyBankAccount(TemplateBankAccount): def __init__(self, manager, owner) -> None: super().__init__(manager, owner, account_type='Company Account') def __str__(self) -> str: return f'{self.account_type}: owner name restricted.' class SavingsBankAccount(TemplateBankAccount): def __init__(self, manager, owner) -> None: super().__init__(manager, owner, account_type='Savings Account') def __str__(self) -> str: return f'{self.account_type}: {self.owner}' class AccountCreator: def __init__(self) -> None: self.manager_group = AccountsManager() def create_account(self, owner_name, account_type): # 存储类引用,而非预先实例化对象 allowed_types = { 'Personal': PersonalBankAccount, 'Company': CompanyBankAccount, 'Savings': SavingsBankAccount } account_cls = allowed_types.get(account_type) if account_cls: # 仅在需要时实例化指定类型的账户 return account_cls(self.manager_group, owner_name) return 'Non offered account type' def main() -> None: creator = AccountCreator() create_account = creator.create_account acc_01 = create_account('Andrew Wiggins', 'Personal') acc_02 = create_account('NASA Inc.', 'Company') acc_03 = create_account('John Paul Wieczorek', 'Savings') list_of_accounts = [str(account) for account in (acc_01, acc_02, acc_03)] print('\n'.join(list_of_accounts)) if __name__ == '__main__': main()
关键修改点
- 将
allowed_types字典的值从实例化后的账户对象改为账户类本身,避免提前触发账户初始化逻辑。 - 在确认用户指定的类型有效后,再调用类构造函数实例化账户,确保每次
create_account仅执行一次账户初始化,对应消耗1个account_id和1个allowed_numbers元素。
这样修改后,三次create_account调用仅消耗3个allowed_numbers元素,剩余2个不会触发异常,同时number()和account_id()的调用次数与账户创建次数一致(各3次)。
内容的提问来源于stack exchange,提问作者CRswr
相关产品推荐
相关产品推荐

