-1

I (accidentally) wrote the following code:

#!/usr/bin/python3.4

class Account:

    balance = 1000

    def withdraw(self, amount):
        print('Withdrawing')
        if(amount > self.balance):
            print("You cannot withdraw that amount!")
        else:
            self.balance -= amount

    def checkBalance(self):
        if (self.balance == 0):
            print("Your account is empty.")
        else:
            print("You still have ", str(self.balance) + "€ euros in your account!")


def Main():
    account1 = Account()
    account2 = Account()

    account1.withdraw(100)
    account1.withdraw(300)

    account1.checkBalance()
    account2.checkBalance()


if __name__ == "__main__":
    Main()

output:

Withdrawing
Withdrawing
You still have 600€ euros in your account!
You still have 1000€ euros in your account!

My questions:

1) why the balance variable does not behave as a class (static) variable although it has been declared as such?

2) even if the usage of the self keyword when accessing the balance variable from within the methods is preventing the static behavior, at which point is an instance copy of the class variable balance made? (at least that is what I am inferring, that each object gets its own copy of the -what is supposed to be a class variable- balance)

3) by trial and error, I discovered that a way to get the (expected?) behavior is to annotate the methods with @classmethod. Is this s.th like in Java that prevents access of static variables from non-static methods?

I am aware of this and this posts but IMHO they do not address the above implications.

1 Answer 1

4

The trick is here:

self.balance -= amount

This actually translates to:

self.balance = self.balance - amount

Now, the complicated thing is that self.balance is not actually the same thing on either side of that assignment. The RHS is evaluated first: Python tries to find an instance variable, fails, and falls back to the class variable. But now it does the LHS, and here it simply does what it normally does: assigns directly to an instance variable called balance.

The point is that the LHS is unaware that the original value came from a class variable; it simply assigns where it is told, to self.balance, which is an instance var.

Of course from then on, the instance var exists, so any further reads of self.balance will get that rather than the original class variable: the class variable exists unchanged, but is shadowed by the instance variable.

When you define the method as a classmethod, the first parameter is no longer the instance (self) but the class itself; for this reason it is customary to call that argument cls. So you read and write directly to the class variable.

Sign up to request clarification or add additional context in comments.

4 Comments

Exactly what I would have said, if I could have expressed it so elegantly. If self.balance were replaced with Account.balance then it would behave as the OP expected.
perhaps mention that the class variable still exists, it's just that it's not being refered to in the self.checkBalance method.
So when python fails to find an instance variable self.x, it falls back to looking up for a class variable x ? In that case that actually takes place in the comparison if amount > self.balance. which precedes the statement with the decrement operator. Also: is the @classmethod annotation preventing the creation of instance variable DESPITE the existence of the self. ? Is the annotation instructing the interpreter to ignore anything before (including) the . ?
Well, it does it everywhere, but the interesting stuff is what I describe happening in the assignment statement. And no, the point is that what you're calling self in the classmethod is not really self but cls: it's the class, not an instance. Don't forget self and cls are just conventions, you can use whatever names you like, but it's best to be clear that in the case of a classmethod the first param is the class object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.