Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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!

share|improve this question
1  
What's the intuition for the way other languages work? Different languages do things differently, it looks like you're just used to the other way. –  Ismail Badawi Nov 14 '12 at 18:45
7  
It only seems wrong because you bring baggage. It's actually logical: after class ...:, the fields are class variables. Only inside an instance initializer does it make sense to declare instance variables. Sure, it's not the C++/Java way of thinking, but it's perfectly understandable. –  Ted Hopp Nov 14 '12 at 18:46
    
I recommend you read this article: nvie.com/posts/unexpected-side-effects-in-python-classes –  Andrey Antukh Nov 14 '12 at 18:46
4  
There's no "default behavior". There's just one of the alternatives somewhat resembling a syntax available in other languages (and probably not intentionally). To me, the behavior of that syntactical feature makes a lot of sense: A class attribute is an attribute bound at class level, and defining one is done by assigning to at the level of the class definition -- at the same level as methods, which also live in the class. It's also perfectly in line with the fact that there are no declarations and everything looking like a declaration is, at every level, a statement. –  delnan Nov 14 '12 at 18:47
2  
I had the same problem coming from Java ;) But I thought of it that way: If you don't have visibility modifiers like static, private, public, how could you tell the scopes of variables apart? You state it explicitly: self.<variablename> = <value> ... the variable <variablename> from scope/namespace self gets the value <value>. Object Orientation is just a concept, it is up to language designers how they interpret/implement it. –  das_weezul Nov 14 '12 at 19:00

2 Answers 2

up vote 8 down vote accepted

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 via ClassName(params)), it receives a new instance of the class via the self parameter, 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 PATH resolution 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):

instance; class; superclass; superclass; ... and so on

This really isn't so different from name resolution in nested scopes in c or c++. You complain:

Even worse is that it seems you are allowed to have an instance variable and a class variable with the same name.

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 another int i; that masks the original?

share|improve this answer
    
This is pretty much the same answer that I wanted to write, but didn't have time. Nice job. –  mgilson Nov 14 '12 at 19:38

I agree that it's counter-intuitive, but it's got a good use -- aliasing functions.

This is from a library I wrote a while back, edited down for length

...
class UndrawnTurtle():
    """Acts just like the turtle package, but without bringing up a window to show it."""
...

    def forward(self, distance):
        angle_radians = math.radians(self.angle)

        self.x = self.x + math.cos(angle_radians) * distance
        self.y = self.y + math.sin(angle_radians) * distance

        self._visit()

...
    # Now for some aliases. Everything that's implemented in this class
    # should be aliased the same way as the actual api.
    fd = forward
...
share|improve this answer
    
And that's not the only good use. Class attributes have uses, just like static variables in static OOP languages have uses. (The uses are different though: Often, a static variable should really be module-level, just like static methods should often be a module-level function.) –  delnan Nov 14 '12 at 19:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.