Multiple calling @property that reduce list

Question:

I’m training my skills of design patterns. With a Factory schema i tried to get and pop example numbers from a imited list.

With initialization of seccond account i recieved an IndexError. In debug mote i noticed that between initialisation of acc01 and acc02 I have a 4 uses of AccountManager.number() func. Same with AccountManager.account_id().

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) -> None:
        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(self.manager_group, owner_name),
                         'Company': CompanyBankAccount(self.manager_group, owner_name),
                         'Savings': SavingsBankAccount(self.manager_group, owner_name)}

        return allowed_types.get(account_type, '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()

I do not know how to change code to get values from self._last_id_number and self._allowed_numbers
only once per create_account call.

Asked By: CRswr

||

Answers:

The problem is your factory method (create_account) creates all objects and it’s not what you want to do – you need to create only the one that is requested. The simplest factory looks like:

    def create_account(self, owner_name, account_type):
        if account_type == "Personal":
            return PersonalBankAccount(self.manager_group, owner_name)

        if account_type == "Company":
            return CompanyBankAccount(self.manager_group, owner_name)

        if account_type == "Savings":
            return SavingsBankAccount(self.manager_group, owner_name)

        return "Non offered account type"

Or with dict that stores the constructors:

    def create_account(self, owner_name, account_type):
        allowed_types = {
            "Personal": PersonalBankAccount,
            "Company": CompanyBankAccount,
            "Savings": SavingsBankAccount,
        }

        if account_type in allowed_types:
            return allowed_types.get(account_type)(self.manager_group, owner_name)

        return "Non offered account type"
Answered By: kosciej16

It ended as:

class AccountCreator:
    def __init__(self) -> None:
        self.manager_group = AccountsManager()

    def create_account(self, owner_name, account_type) -> Any | str:
        allowed_types = {'Personal': PersonalBankAccount,
                         'Company': CompanyBankAccount,
                         'Savings': SavingsBankAccount}

        account = allowed_types.get(account_type, None)

        if account is not None:
            return account(self.manager_group, owner_name)

        return "Non offered account type"
Answered By: CRswr
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.