346

I noticed that in Python, people initialize their class attributes in two different ways.

The first way is like this:

class MyClass:
  __element1 = 123
  __element2 = "this is Africa"

  def __init__(self):
    #pass or something else

The other style looks like:

class MyClass:
  def __init__(self):
    self.__element1 = 123
    self.__element2 = "this is Africa"

Which is the correct way to initialize class attributes?

peterh
  • 11,875
  • 18
  • 85
  • 108
jeanc
  • 4,563
  • 4
  • 22
  • 27
  • 8
    the diffence is not big if you use strings ... but it will get a complete different thing if you use dicts or lists that are stored by reference – Bastian Jan 29 '12 at 22:02

2 Answers2

735

Neither way is necessarily correct or incorrect, they are just two different kinds of class elements:

  • Elements outside the __init__ method are static elements; they belong to the class.
  • Elements inside the __init__ method are elements of the object (self); they don't belong to the class.

You'll see it more clearly with some code:

class MyClass:
    static_elem = 123

    def __init__(self):
        self.object_elem = 456

c1 = MyClass()
c2 = MyClass()

# Initial values of both elements
>>> print c1.static_elem, c1.object_elem 
123 456
>>> print c2.static_elem, c2.object_elem
123 456

# Nothing new so far ...

# Let's try changing the static element
MyClass.static_elem = 999

>>> print c1.static_elem, c1.object_elem
999 456
>>> print c2.static_elem, c2.object_elem
999 456

# Now, let's try changing the object element
c1.object_elem = 888

>>> print c1.static_elem, c1.object_elem
999 888
>>> print c2.static_elem, c2.object_elem
999 456

As you can see, when we changed the class element, it changed for both objects. But, when we changed the object element, the other object remained unchanged.

Nathaniel Jones
  • 939
  • 1
  • 14
  • 25
juliomalegria
  • 24,229
  • 14
  • 73
  • 89
  • 2
    but being __init__ always executed after the object is created, it becomes practically equal to define variables outside of __init__, right? – jeanc Jan 29 '12 at 21:33
  • the big difference is that the static elements belong to the class, not to the object. I'll edit my answer with a little bit more explanation. – juliomalegria Jan 29 '12 at 21:37
  • 3
    If you change a class attribute (one defined outside `__init__()`) it changes for the whole class. It will change for other instances too whereas instance attributes (defined in `__init__()`) are specific to each instance. – nitsas Jan 29 '12 at 21:46
  • there might be a difference if you inherit from this class .. you could overwrite __init__() and don't call it on super. – Bastian Jan 29 '12 at 21:52
  • 1
    I'd remove the double leading underscores in attribute names. They throw **AttributeError** (in my system at least). They invoke Python's name mangling according to [PEP 8](http://www.python.org/dev/peps/pep-0008/) which means you'll have problems when subclassing. If you need to indicate they are private, just add a single leading underscore. – nitsas Jan 29 '12 at 21:53
  • @chrisn654 I suggest you to read more about the Python name mangling, it's actually very interesting (not problematic) – juliomalegria Jan 29 '12 at 22:52
  • 1
    This was very helpful. It's really important to understand that things outside __init__ are static. I did not realize this and it caused some very interesting results in my code. Unless you really want it to be static, just define everything inside __init__. I thought outside init was equivalent to inside init, They are VERY different. – Jon Mar 31 '15 at 18:32
  • Thanks nitsas for the comment, It helps me to understand the "instance has no attribute" error message. – Arsen Apr 25 '16 at 19:36
  • 99
    Not totally accurate: `static_elm` changes for all class instances when assigned to via the class (`MyClass.static_element = X`), as shown. But when assigned to via a class instance, then, _for that instance_, `static_elm` will become an instance member. If you do: `c1.static_elem = 666`, then `print c1.static_elem, c2.static_elem` will produce `666, 999`. From within the class, `self.static_elm` returns `MyClass.static_elm` _until assigned_ `self.static_elm = X`. Then, _a new_ `self.static_elm` is created, obscuring the class variable (still reachable through `MyClass.static_elm`) – Lobotomik Mar 08 '17 at 11:02
  • 3
    Lobotomik made a clear explanation. Yes, that's a strange design on Python, it's very easy to confuse on this feature designed in Python, because the class element could be suddenly changed to be an instance's element. – Clock ZHONG Apr 27 '18 at 10:57
  • @juliomalegria Perhaps an additional question on class variables. I noted that if a class is inherited, the derived class can add variables and they can be referenced by the base class with `self.inst_var`. However, if in the derived class, a class variable is defined, say `clas_var_deriv`. It can be only referenced by the base class as `self.clas_var_deriv`. Why is this the case? Kind of class variables of the the derived class become instance variables of the base class. – Alexander Cska Jun 26 '18 at 08:35
  • 7
    @Lobotomik: I believe it is only "shadowing" the class variable, since you can still access the unmodified class variable via c1.__class__.static_elem. Your words "static_elm will become an instance member" are understood by me in the sense that static_elem will change from class variable to instance variable. This is not the case. – gebbissimo Jan 28 '19 at 10:04
  • 1
    I was so shocked when I kept getting an error because everythin seemed right and after two hours of looking i found out that i had __init__ as __inti__ ! – Kaleba KB Keitshokile Apr 24 '19 at 04:21
  • 1
    As @gebbissimo mentioned, at the moment of initializing the c1.static_elem, the instance static_elem field is created on the fly which "shadows" MyClass.static_element static field in c1 instance. But static static_elem field does not accidentally change its status to being instance field and stays static, being accessible with c1.__class__.static_elem – Victor Di Feb 22 '22 at 17:08
22

I think this sample explains the difference between the styles:

james@bodacious-wired:~$cat test.py 
#!/usr/bin/env python

class MyClass:
    element1 = "Hello"

    def __init__(self):
        self.element2 = "World"

obj = MyClass()

print dir(MyClass)
print "--"
print dir(obj)
print "--"
print obj.element1 
print obj.element2
print MyClass.element1 + " " + MyClass.element2
james@bodacious-wired:~$./test.py 
['__doc__', '__init__', '__module__', 'element1']
--
['__doc__', '__init__', '__module__', 'element1', 'element2']
--
Hello
World
Traceback (most recent call last):
  File "./test.py", line 17, in <module>
    print MyClass.element2
AttributeError: class MyClass has no attribute 'element2'

element1 is bound to the class, element2 is bound to an instance of the class.

Ben
  • 2,348
  • 1
  • 20
  • 23
James Polley
  • 7,977
  • 2
  • 29
  • 33
  • I'm unable to edit or suggest edits. The output for `obj.element1` and `obj.element2` is messed up. – Git Gud Jan 05 '22 at 23:36