Usefulness of def __init__(self)?

Question:

I am fairly new to python, and noticed these posts:
Python __init__ and self what do they do? and
Python Classes without using def __init__(self)

After playing around with it, however, I noticed that these two classes give apparently equivalent results-

class A(object):
    def __init__(self):
        self.x = 'Hello'

    def method_a(self, foo):
        print self.x + ' ' + foo

(from this question)

and

class B(object):
    x = 'Hello'
    def method_b(self,foo):
        print self.x + ' ' + foo

Is there any real difference between these two? Or, more generally, does __init__ change anything inherently about the attributes of a class? In the documentation it is mentioned that __init__ is called when the instance is created. Does this mean that x in class B is established before instantiation?

Asked By: Shaun

||

Answers:

Yeah, check this out:

class A(object):
    def __init__(self):
        self.lst = []

class B(object):
    lst = []

and now try:

>>> x = B()
>>> y = B()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1, 2]
>>> x.lst is y.lst
True

and this:

>>> x = A()
>>> y = A()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1]
>>> x.lst is y.lst
False

Does this mean that x in class B is established before instantiation?

Yes, it’s a class attribute (it is shared between instances). While in class A it’s an instance attribute. It just happens that strings are immutable, thus there is no real difference in your scenario (except that class B uses less memory, because it defines only one string for all instances). But there is a huge one in my example.

Answered By: freakish

In the first exemple you have the variable of the instance of the class. This variable is only accessible through an instance (self required).

class A():
    def __init__(self):
        self.x = 'hello'

print A.x -> AttributeError
print A().x -> 'hello'

In the second exemple you have a static variable. You can access to this variable thanks to the name of the class A

class A():
  x = 'hello'

print A.x -> 'hello'
print A().x -> 'hello'

In fact you can have a static variable and an instance variable with the same name:

class A():
    x = 'hello'
    def __init__(self):
        self.x = 'world'

print A.x -> hello
print A().x -> world

The static value is shared between all the instances

class A():
    x = 'hello'

    @staticmethod
    def talk():
        print A.x

a = A()
print a.talk() -> hello

A.x = 'world'
print a.talk() -> world

You have a good article here:
http://linuxwell.com/2011/07/21/static-variables-and-methods-in-python/

Answered By: Julio

As others have stated, it’s the difference between a variable on a class and a variable on a class instance. See the following example.

>>> class A:
...     a = []
... 
>>> class B:
...     def __init__(self):
...         self.b = []
... 
>>> a1 = A()
>>> a1.a.append('hello')
>>> a2 = A()
>>> a2.a
['hello']
>>> b1 = B()
>>> b1.b.append('goodbye')
>>> b2 = B()
>>> b2.b
[]

For immutable objects like tuples, strings, etc. it’s harder to notice the difference, but for mutables, it changes everything—the changes applied are shared between ALL instances of that class.

Note also that the same behavior happens for keyword argument defaults!

>>> class A:
...     def __init__(self, a=[]):
...         a.append('hello')
...         print(a)
... 
>>> A()
['hello']
>>> A()
['hello', 'hello']
>>> A()
['hello', 'hello', 'hello']

>>> class B:
...     def __init__(self, b=None):
...         if b is None:
...             b = []
...         b.append('goodbye')
...         print(b)
... 
>>> B()
['goodbye']
>>> B()
['goodbye']
>>> B()
['goodbye']

This behavior bites a lot of new Python programmers. Good for you in discovering these distinctions early on!

Answered By: Thane Brimhall
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.