2

The way I usually declare a class variable to be used in instances in Python is the following:

class MyClass(object):
  def __init__(self):
    self.a_member = 0

my_object = MyClass()
my_object.a_member    # evaluates to 0

But the following also works. Is it bad practice? If so, why?

class MyClass(object):
  a_member = 0

my_object = MyClass()
my_object.a_member    # also evaluates to 0

The second method is used all over Zope, but I haven't seen it anywhere else. Why is that?

Edit: as a response to sr2222's answer. I understand that the two are essentially different. However, if the class is only ever used to instantiate objects, the two will work he same way. So is it bad to use a class variable as an instance variable? It feels like it would be but I can't explain why.

eje211
  • 2,385
  • 3
  • 28
  • 44
  • possible duplicate of [Python variable declaration](http://stackoverflow.com/questions/11007627/python-variable-declaration) – detly Aug 20 '12 at 03:05
  • Here's another similar question, check it out: http://stackoverflow.com/questions/207000/python-difference-between-class-and-instance-attributes – mjgpy3 Aug 20 '12 at 03:07

4 Answers4

7

The question is whether this is an attribute of the class itself or of a particular object. If the whole class of things has a certain attribute (possibly with minor exceptions), then by all means, assign an attribute onto the class. If some strange objects, or subclasses differ in this attribute, they can override it as necessary. Also, this is more memory-efficient than assigning an essentially constant attribute onto every object; only the class's __dict__ has a single entry for that attribute, and the __dict__ of each object may remain empty (at least for that particular attribute).

In short, both of your examples are quite idiomatic code, but they mean somewhat different things, both at the machine level, and at the human semantic level.


Let me explain this:

>>> class MyClass(object):
...       a_member = 'a'
...
>>> o = MyClass()
>>> p = MyClass()
>>> o.a_member
'a'
>>> p.a_member
'a'
>>> o.a_member = 'b'
>>> p.a_member
'a'

On line two, you're setting a "class attribute". This is litterally an attribute of the object named "MyClass". It is stored as MyClass.__dict__['a_member'] = 'a'. On later lines, you're setting the object attribute o.a_member to be. This is completely equivalent to o.__dict__['a_member'] = 'b'. You can see that this has nothing to do with the separate dictionary of p.__dict__. When accessing a_member of p, it is not found in the object dictionary, and deferred up to its class dictionary: MyClass.a_member. This is why modifying the attributes of o do not affect the attributes of p, because it doesn't affect the attributes of MyClass.

bukzor
  • 37,539
  • 11
  • 77
  • 111
  • I figured part of it myself, and I posted as much below. But your answer answers all of my questions about this topic. Now it looks so obvious that I cannot believe how baffled I was by it 20 minutes ago. Thanks! This is exactly what I needed! – eje211 Aug 20 '12 at 03:32
4

The first is an instance attribute, the second a class attribute. They are not the same at all. An instance attribute is attached to an actual created object of the type whereas the class variable is attached to the class (the type) itself.

>>> class A(object):
...     cls_attr = 'a'
...     def __init__(self, x):
...         self.ins_attr = x
...
>>> a1 = A(1)
>>> a2 = A(2)
>>> a1.cls_attr
'a'
>>> a2.cls_attr
'a'
>>> a1.ins_attr
1
>>> a2.ins_attr
2
>>> a1.__class__.cls_attr = 'b'
>>> a2.cls_attr
'b'
>>> a1.ins_attr = 3
>>> a2.ins_attr
2
Silas Ray
  • 25,682
  • 5
  • 48
  • 63
  • I understand that. But if the class is only ever used to instantiate objects, it will still work. So is it bad to do it? If so why? – eje211 Aug 20 '12 at 03:01
  • So you are making a factory class? What does it do that requires state so that you couldn't just make it a function? – Silas Ray Aug 20 '12 at 03:07
  • I tried an equivalent piece of code before posting but when I tried it, the class attribute didn't change between instances: http://pastebin.com/Wq6jaLXm . What did I do differently? – eje211 Aug 20 '12 at 03:13
  • @eje211 I did the same here, with the same results. Confused! – heltonbiker Aug 20 '12 at 03:14
  • @heltonbiker Which one did you try? Mine or sr2222's? Or both? – eje211 Aug 20 '12 at 03:15
  • I tried another example with an __init__ and the class attributes still differed between instances. – eje211 Aug 20 '12 at 03:17
  • I created two objects with a class which contained an attribute, and when I change that class attribute in one instance, the other doesn't get changed. The code is similar to the link posted by eje211. – heltonbiker Aug 20 '12 at 03:23
  • What's happening in eje211's case is assigning a new value to an instance _creates an instance attribute_ which shadows the class one. The only thing you need to be concerned with propagating attribute changes to all instances is direct modifications to the class attribute via the class itself. – Matthew Trevor Aug 20 '12 at 03:28
  • @MatthewTrevor Thanks, yup. Fixed. – Silas Ray Aug 20 '12 at 13:24
0

Even if you are never modifying the objects' contents, the two are not interchangeable. The way I understand it, accessing class attributes is slightly slower than accessing instance attributes, because the interpreter essentially has to take an extra step to look up the class attribute.

Instance attribute

"What's a.thing?"

Class attribute

"What's a.thing? Oh, a has no instance attribute thing, I'll check its class..."

Matthew Adams
  • 9,426
  • 3
  • 27
  • 43
0

I have my answer! I owe to @mjgpy3's reference in the comment to the original post. The difference comes if the value assigned to the class variable is MUTABLE! THEN, the two will be changed together. The members split when a new value replaces the old one

>>> class MyClass(object):
...   my_str = 'a'
...   my_list = []
... 
>>> a1, a2 = MyClass(), MyClass()
>>> a1.my_str # This is the CLASS variable.
'a'
>>> a2.my_str # This is the exact same class variable.
'a'
>>> a1.my_str = 'b' # This is a completely new instance variable. Strings are not mutable.
>>> a2.my_str # This is still the old, unchanged class variable.
'a'
>>> a1.my_list.append('w') # We're changing the mutable class variable, but not reassigning it.
>>> a2.my_list # This is the same old class variable, but with a new value.
['w']

Edit: this is pretty much what bukzor wrote. They get the best answer mark.

eje211
  • 2,385
  • 3
  • 28
  • 44
  • It doesn't have to be a mutable *object* to make a difference. A class attribute means that with a reference to the class you can alter the value of the attribute as looked up in *all* its instances. Even if the object itself is immutable, you can still change which object is referenced by the class attribute. Using an instance attribute means that the instances all have their own copy; the only way to globally update them all would be to actually find them all and update each one individually. – Ben Aug 20 '12 at 04:50
  • The original question supposed that after the class had been defined, it would then ONLY be used for instantiation. So, if I'm never touching the class again and the member is immutable, then I cannot change it in a way that will affect other object, can I? – eje211 Aug 20 '12 at 07:04
  • That's fine for this case, then. I mainly commented so that nobody would read this answer and think that mutability of the object was the key factor *in general*. – Ben Aug 20 '12 at 07:45