4

I'm new to Python and am perplexed by some behavior I'm seeing. If I define

class Obj():
    a = 1
    b = 2

and then create an instance

x = Obj()

all is well:

print(Obj.__dict__)
print(x.__dict__)
print(x.a,Obj.a)

produces

{'a': 1, '__module__': '__main__', 'b': 2, '__doc__': None}
{}
(1, 1)

But if I

x.a = 20

I get

{'a': 1, '__module__': '__main__', 'b': 2, '__doc__': None}
{'a': 20}
(20, 1)

I understand that, initially, a is the class variable Foo.a, and that initially there is no instance variable a. I also understand that, at that point. x.a simply refers to Obj.a. I also understand that, when I assign to x.a, I create an instance variable x.a that masks the class variable Obj.a. But, while I understand that this is (at least partially) consistent with the rest of Python's declaration on assignment, I'm perplexed about why this is allowed for classes? Naively, I'd have expected that either the assignment to x.a should change Foo.a (since that's what it has referred to up to that point) or that referring to x.a prior to the assignment should be an error (e.g., "There's no instance attribute 'a'.").

Why is this behavior allowed. What — for those of us new to Python — does this kind of behavior correspond to in other OO paradigms?

Put another way: I can't think of any where you can say x.a and have it mean Obj.a and then, in the next line, say x.a = ... and have it no longer mean Obj.a. It's possible though that I'm just not remembering, and an example of that -- or confirmation that there's no such thing -- would be a perfect answer.

3
  • Note that there are many answers about what is happening here; this question is about why (given the obvious potential for bugs) and, especially, how to think about this in OO terms. Commented Dec 24, 2012 at 17:13
  • It sounds like you're expecting assignment to x.a to mutate the class of x. In reality it mutates the instance x, but not the class; any new instances of Obj will conform to the template in the class. Could you go into a little more detail to explain the logic that led you to this assumption? Commented Dec 24, 2012 at 17:22
  • @poorsod: I've added a bit more to clarify the kind of answer I'm looking for. Commented Dec 24, 2012 at 17:27

1 Answer 1

5

You have perfectly summarized the semantics. It works this way because:

  1. Looking up an attribute on an object has to return object attributes, how could it not?

  2. Looking up an attribute on an object has to find a class attribute if it doesn't exist on the object, because that's how objects get methods, as well as anything else defined on the class.

  3. Setting an attribute on an object has to create an object attribute.

  4. Because there are no declarations in Python, the only way to create an attribute is to actually set it, so #3 has to set the attribute on the object.

I don't see a way to make 1 through 4 work without having the behavior you describe.

I'm not sure how to describe this in terms of other languages. You say, "how to think about this in OO terms." Don't fall in the trap of assuming that some other language "defines" OO. Object Orientation is done differently in all languages.

Added: You say,

I'd have expected that either the assignment to x.a should change Foo.a (since that's what it has referred to up to that point) or that referring to x.a prior to the assignment should be an error (e.g., "There's no instance attribute 'a'.").

Because Python has no declarations, attributes don't exist in any way until they are assigned to. How would you ever create an object attribute if assigning to an attribute on an object didn't create it? That is, work through the example of a class with no x attribute, and an instance of that class, what would o.x = 17 do?

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

5 Comments

I'd add that you can also delete attributes, and this can lead to some nice corner cases that without the current semantics would lead to many problems or much more complicated semantics.
But that's a bit like saying "because that's how it's implemented". Good point about OO in general, but are there perhaps other examples of languages that do the same thing. I can't think of any where you can say x.a and have it mean Obj.a and then, in the next line, say x.a = ... and have it no longer mean Obj.a. It's possible though that I'm just not remembering, and an example of that would be a perfect answer.
@raxacoricofallapatorius: I don't understand why another language would make you more comfortable with this. Why would "Java does it like xxx" make you happier than, "Python does it like xxx"? Other popular languages, notably C++ and Java, are statically typed, meaning they have declarations to create attributes before any access. Python does not. This is a big difference, and leads to big differences in the rest of the language. I've highlighted this in #4 above.
@NedBatchelder: That's a good point. If it's true that C++, Java, and Smalltalk don't do this (maybe even hate it) then that would be a big part of the answer I'm looking for. Remember, I'm new to Python and am having a bit of a WTF moment: this could cause terrible bugs.
@raxacoricofallapatorius Never heard of bugs due to this. If you are handling a class attribute you already know you must act on the class and not on the instances. The fact that A.attribute might return A.__class__.attribute is done simply because methods are class attributes which are functions, and thus not allowing this look up would lead to code like TheClass.method(the_instance, OtherClass.an_other_method(instance2, value2), value1) instead of the_instance.method(instance2.an_other_method(value2)).

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.