Python Forum
Object already contains values when created
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Object already contains values when created
#1
I am running some unit tests using pytest in PyCharm and am running into a, for me, weird problem.

class BalanceSheet():
    """Balance sheet"""

    __assets: Dict[str, float] = {}
    __liabilities: Dict[str, float] = {}

    def __init__(self, balance_sheet: BalanceSheet = None):
        if balance_sheet is not None:
            for name in balance_sheet.assets.keys():
                self.book_asset(name, balance_sheet.asset(name))

            for name in balance_sheet.liabilities.keys():
                self.book_liability(name, balance_sheet.liability(name))

    @property
    def assets(self) -> Dict[str, float]:
        return self.__assets

    @property
    def liabilities(self) -> Dict[str, float]:
        return self.__liabilities

    def book_asset(self, asset_name: str, amount: float):
        if asset_name in self.__assets:
            self.__assets[asset_name] += amount
        else:
            self.__assets[asset_name] = amount

    def book_liability(self, liability_name: str, amount: float):
        if liability_name in self.__liabilities:
            self.__liabilities[liability_name] += amount
        else:
            self.__liabilities[liability_name] = amount

    def asset(self, name: str) -> float:
        if name in self.assets:
            return self.assets[name]
        else:
            return 0.0

    def liability(self, name: str) -> float:
        if name in self.liabilities:
            return self.liabilities[name]
        else:
            return 0.0


class BalanceSheetTimeline(BalanceSheet):
    __history: List[BalanceSheet] = []

    def clear(self):
        super().clear()
        self.__history.clear()

    def save_state(self) -> bool:
        if self.validate():
            self.__history.append(BalanceSheet(self))

            return True
        else:
            return False
    
    def balance_history(self, time_delta: int) -> BalanceSheet:
        if time_delta == 0:
            return BalanceSheet(self)
        else:
            return self.__history[len(self.__history) - abs(time_delta)]
And I'm running the following pytest on it:

def test_history():
    balance: BalanceSheetTimeline = BalanceSheetTimeline()

    balance.book_asset(DEPOSITS, 100.00)
    balance.book_liability(EQUITY, 100.0)

    balance.save_state()

    history: BalanceSheet = balance.balance_history(-1)
    assert history.asset(DEPOSITS) == 100.0
    assert history.liability(EQUITY) == 100.0
Things go wrong with the first assert on history. For some reason, the DEPOSITS asset equals 200.0. I've been debugging the code and it seems to happen in the constructor which is called from save_state(). The BalanceSheet instance which gets created already contains the DEPOSITS en EQUITY entries before I start copying them over. I have no clue why since the object should be empty, with empty Dict's as far as I know.

Does anyone have any idea what is going on here?

Thanks in advance,
Stef
Reply
#2
    def clear(self):
        super().clear()
and
    def save_state(self) -> bool:
        if self.validate():
i don't see validate and clear methods defined in BalanceSheet?

Edit n.1: I love that you are using typing, just one small correction here:
def __init__(self, balance_sheet: BalanceSheet = None):
should be:
def __init__(self, balance_sheet: Optional[BalanceSheet] = None):
Reply
#3
Indeed, I forgot to copy the validate() and clear() methods of BalanceSheet. Here they are:

    def clear(self):
        self.assets.clear()
        self.liabilities.clear()

    def validate(self) -> bool:
        return self.__value(self.assets) - self.__value(self.liabilities) == 0

    def __value(self, entries: Dict[str, float]) -> float:
        total_value = 0.0

        for value in entries.values():
            total_value += value

        return total_value
Reply
#4
That is a good test. It points out there is a serious error in your code.

BalanceSheetTimeline is a wrapper around a list of BalanceSheet instances. A list indicates there will be more than one BalanceSheet.

BalanceSheet uses a Class Variable to record assets and liabilities. Being a Class Variable means there is only one asset dictionary and one liability dictionary that is shared by all balance sheets.

See the problem?
Reply
#5
@deanhystad

Thank you so much! I'm coming from Java and am fairly new to Python so I'm carrying some old habits with me. I totally was unaware of how class variables and instance variables were treated in Python and assumed it was comparable to Java. Joke's on me ;)
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Printing out incidence values for Class Object SquderDragon 3 270 Apr-01-2024, 07:52 AM
Last Post: SquderDragon
  AttributeError: 'list' object has no attribute 'values' ilknurg 4 14,941 Jan-19-2022, 08:33 AM
Last Post: menator01
  How to mock an object that is created during function call? Schlangenversteher 0 1,968 Jan-31-2020, 01:36 PM
Last Post: Schlangenversteher
  How does a single object see all the values inside it? jenniferruurs 1 1,818 Oct-01-2019, 04:57 PM
Last Post: stullis
  Return Object Created from Multiple Classes emerger 3 3,044 Oct-18-2018, 02:14 AM
Last Post: emerger
  How to iterate a PlainLocationField object and fetch some values? PrateekG 9 4,954 Jun-21-2018, 08:49 AM
Last Post: wavic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020