Class VS instance variables

In the code below I have a class named Category as shown and I created 3 instances
and performed the deposit method on one of them only (c1). I called the balance of c2 and it came as I expected, but when I called the ledger of c2 which I expected to come as [ ], it came the same as if I were to call ledger of c1. Why is that? (if we were to assume that the balance and ledger variables are both of the class type, why aren’t they both (when called with c2) changed according to the method deposit performed on c1, why is it just ledger that is affected?)

class Category:
    balance = 0
    ledger = []

    def __init__(self, category_name):
        self.category_name = category_name

    def deposit(self, amount, description=""):
        self.amount = amount
        self.description = description
        self.balance += self.amount
        self.ledger.append({'amount': self.amount, 'description': self.description})

c1 = Category("clothing")
c2 = Category("food")
c3 = Category('entertainment')
c1.deposit(500, "from working in Italy")


So I think you are supposed to use “self.ledger”. This is because the “self” part means that the ledger is only attached/bound to its ‘instance’.
However in your code, the “ledger” is used by all the instances you created.

I see it like the “self” is a placeholder for for the specific “instance”.

For example:

class Category:

def __init__(self, category_name):
    self.list = []

def add(self, amount):

a1 = Category(“clothing”)
c2 = Category(“food”)
print("a1 list: ", a1.list)
print("c2 list: ", c2.list)


a1 list: [23]
c2 list: [ ]

In this case, each instance has its own list. In “self.list”, the “self” part becomes attached to the “a1” or the “c2” part.

There’s few aspects here. Both balance and ledger are class variables. To fully use them as such proper syntax would be Category.balance and Category.ledger. Referring to them as self.balance and self.ledger adds some peculiarity to the behavior here.

Generally when trying to use attribute as self.variable_name, python will first check that name among instance variables. After that among the class variables (and then in parent classes of the class, if they exist).

Because ledger is a list and at any point isn’t rewritten, but just mutated self.ledger still points to the original list from the class variable.

With balance, in this case specifically, is a bit more complicated. This line is really two steps: self.balance += self.amount.

First reading from self.balance, then saving result of addition with self.amount to the self.balance. Because instance doesn’t have balance variable, balance name is searched among class variables, from where, the initial 0 is obtained. The self.amount is added to the 0, the result of it is assigned to the self.balance. However this time python doesn’t (need to) look in the class variables, because that’s just an assignment and the result is saved as an instance variable.

I tried that already, thanks for the comment, but my question is why the balance variable acted like an instance variable while ledger didn’t ( with the note that they both are class variables).

Thanks for clarifying, but still I got some questions on your reply, hope you don’t mind.

  1. How can the ledger be rewritten in your explanation above and how would that affect it being searched among class or instance variables?

  2. In the end, if I understood you correctly, you mention that Python looked up the self.balance value among the class variables and added it with the self.amount variable and then assigned the result to ( self.balance) but this time self.balance is considered as an instance variable that is different from the class variable or what, I am really confused.

  1. For example if instead of self.ledger.append(...) there would be
self.ledger = self.ledger + [{'amount': self.amount, 'description': self.description}]`

Then ledger would behave similarly to the balance variable.

  1. It is confusing. The gist of it is that when variable is read it can be read from different places (instance variables, class variables, etc.), but when it is written then it’s written, well, literally where code says. self refers to instance, so it’s written as instance variable. Using Category.balance would write it as class variable.

Having said that, usually it’s better to default to using instance variables. Unless there’s a specific reason why class variable is used.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.