You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

工厂模式下@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:15:23