This plagued me for hours, as I am from the C++ world. I finally found out what was going on, but I do not know why this is the default behaviour. I’d like to understand why the language is designed this way.
I wanted an instance variable mem. So I tried this:
class x(object):
mem = []
obj = x()
obj.mem.append(1)
print(obj.mem)
objTWO = x()
objTWO.mem.append(2)
print(objTWO.mem)
Which prints this:
[1]
[1, 2]
Whereas:
class x(object):
def __init__(self):
self.mem = []
obj = x()
obj.mem.append(1)
print(obj.mem)
objTWO = x()
objTWO.mem.append(2)
print(objTWO.mem)
prints
[1]
[2]
Why would the first be the default behaviour? What is the intuition here, since its the opposite of how many mainstream OO languages work (they introduce the static keyword for the top case, which makes you explicitly say you want a static variable)? To newcomers to Python, this is a surprise.
Also, it seems you are allowed to have an instance variable and a class variable with the same name:
class x(object):
mem = []
def __init__(self):
self.mem = []
I would have to run this to figure out what would be printed. I can’t even guess!
The intuition is that in Python, everything is an object, including classes themselves. There’s no such thing as a “static” keyword in Python; there are classes, which are objects, and those classes have attributes. Everything that appears in the class definition is a class attribute — that includes both methods and other kinds of attributes.
The benefit of this design is simplicity and consistency. There’s no distinction between public or private, or between static and non-static attributes. The distinction between class and instance attributes emerges naturally through the class constructor. When
__init__is called (indirectly viaClassName(params)), it receives a new instance of the class via theselfparameter, and it then modifies that instance directly. Everything happens explicitly, through constructs that are already defined — you don’t have to understand any new syntax or new keywords to see what’s happening when an instance is created.Then all you have to understand is Python’s model for attribute lookup. It works almost identically to
PATHresolution in most shells. In bash, for example, when you execute a command (ls), the first entry in the path is searched, then the second and so on, and the first version of the command that’s found is the one that’s executed. But in this case, the path looks like this (at least in simple cases with single inheritance):This really isn’t so different from name resolution in nested scopes in c or c++. You complain:
But is that really any more confusing than the fact that c allows you to define
int i;in a block, create an inner block, and then define anotherint i;that masks the original?