Question on a Python Problem

Hi. I am currently reviewing some sample exam questions for Python.
And for below question, I am confused why it is 3?
Thank you.

class Class:
    __Var = 0
    def foo(self):
        Class._Class__Var += 1
        self.__prop = Class._Class__Var

o1 = Class()
o1.foo()
o2 = Class()
o2.foo()
print(o2._Class__Var + o1._Class__prop)
class Class:
    __Var = 0  

Class variables which are preceded by two underscores are name mangled by prefixing the name of the class. __Var therefore becomes _Class_Var.

    def foo(self):
        Class._Class__Var += 1
        self.__prop = Class._Class__Var

Class._Class__Var is a class variable, i.e. it is the same across all instances of the class (and the class itself). When it is incremented in any instance, it is incremented in all instances.

self.__prop is similarly name mangled to self._Class__prop, but it is still just a simple instance variable. Its value is individual for each instance.

o1 = Class()
o1.foo()

Here, an instance of Class is created (o1). When calling the instance’s foo() method, the _Class_Var class variable is incremented from 0 to 1 for both the instance and the class itself. o1._Class__prop is set to the current value of Class._Class__Var, which is 1.

o2 = Class()
o2.foo()

Another instance of Class is created (o2). When calling foo(), Class._Class__Var is again incremented, this time from 1 to 2. o2._Class__prop is set to the current value of Class._Class__Var, which is 2.

print(o2._Class__Var + o1._Class__prop)

Per above, o1._Class_prop == 1 and o2._Class__Var == 2. Their sum is 3.

1 Like

I am still not 100% clear with the mechanisms of a class variable (i.e. incremented in all instances, meaning for subsequent instances as well)… but I shall review this chapter and hopefully be enlightened :slight_smile:

Thank you so much for this explanation! Appreciate it lot!

Remember that a class is itself an object, and has its own attributes. So we have two simple cases, and then one that’s slightly more complicated. Here’s a bit of a breakdown:

class Demo:
    cls_int = 42
    cls_list = ["hello"]
    def __init__(self):
        self.inst_int = 123
        self.inst_list = ["goodbye"]

demo_instance = Demo()

Okay. So we have two attributes on the class, and two on the instance. One’s an immutable integer, the other’s a mutable list. Let’s look at the two easy cases first.

print(Demo.cls_int, Demo.cls_list)
print(demo_instance.inst_int, demo_instance.inst_list)
Demo.cls_int = 142857
Demo.cls_list.append("world")
demo_instance.inst_int = 314159
demo_instance.inst_list.append("felicia")

All of this should be completely unsurprising. When you either read from or assign to an attribute of some object - even if that object happens to be a class - it’s going to behave just the way you expect it to.

The less obvious situation, though, is that an instance can see class attributes. Let’s start with read-only usage:

print(demo_instance.cls_int, demo_instance.cls_list)
# Prints out 142857 and ["hello", "world"] if you did the above
# mutations, otherwise 42 and ["hello"]

However, any time you assign, it goes to the instance.

demo_instance.cls_int = 271828
print(Demo.cls_int, demo_instance.cls_int)
# Prints 142857 and 271828

You now should have all the tools you need to work through these questions.

  1. What happens if you do demo_instance.cls_list.append("!") ? Which list is mutated?
  2. If you did the assignment of cls_int to 271828, do del demo_instance.cls_int. Now, what happens if you do demo_instance.cls_int = demo_instance.cls_int + 1 ? Remember, it’s a lookup (and the attribute won’t exist on the instance), followed by an assignment.
  3. Delete the attribute again if you need to. What does demo_instance.cls_int += 100 do? How does the += operator work with integers?
  4. Now for a tricky one.
    a. print(demo_instance.cls_list)
    b. demo_instance.cls_list += ["Wut"]
    c. print(demo_instance.cls_list)
    d. print(Demo.cls_list)

What has happened after step 4? Which object(s) have an attribute named cls_list ? What do those attributes contain? This is a bit tricky, so feel free to play around in the REPL and try to figure it out. Hint: You can check what attributes something has with the dir() function, and make use of the is operator to see whether two objects are actually the same object.

Bonus challenge: What would be different if, instead of a ilst, we’d used a tuple? You can demo_instance.cls_tuple += ("Wut?",) to have the same effect as the list concatenation. How would that change what happens here?

(Edit: Fixed a bug in the question.)

1 Like