_Init_ as a Constructor

__init__ as a constructor?

If you have a class Foo then:

  • Foo() is the constructor
  • Foo.__init__() is the initializer
  • Foo.__new__() is the allocator

Construction of a Python object is simply allocation of a new instance followed by initialization of said instance.

Python constructors and __init__

There is no function overloading in Python, meaning that you can't have multiple functions with the same name but different arguments.

In your code example, you're not overloading __init__(). What happens is that the second definition rebinds the name __init__ to the new method, rendering the first method inaccessible.

As to your general question about constructors, Wikipedia is a good starting point. For Python-specific stuff, I highly recommend the Python docs.

Is a constructor __init__ necessary for a class in Python?

I see a misconception here between a constructor--constructing the object and initializing the object:

Python's use of __new__ and __init__?

Use __new__ when you need to control the creation of a new instance. Use __init__ when you need to control initialization of a new instance.

So we must be careful here.

I read that the constructor is like the first argument passed to the class, which makes sense to me since the parameters seem to be passed to the class via the __init__ method.

The constructor is not passed to the class, to be precise the result of the constructor (__new__) will be the first argument for every instance method in the class or its sub-classes (note: __new__ works only for new-style classes):

class A:
def __new__(self):
return 'xyz'

See what happens when you call the class (create the object):

>>> A()
'xyz'
>>> type(A())
<class 'str'>

Calling the class no longer return instance of type A, because we changed the mechanism of the constructor __new__. Actually by doing so you alter the whole meaning of your class, not only, this is pretty much hard to decipher. It's unlikely that you'll switch the type of object during the creating time of that specific object. I hope this sentence makes sense, if not, how will it make sense in your code!

class A:
def __new__(self):
return 'xyz'

def type_check(self):
print(type(self))

Look what happens when we try to call type_check method:

>>> a = A()
>>> a
'xyz'
>>> a.type_check()
AttributeError: 'str' object has no attribute 'type_check'

a is not an object of class A, so basically you don't have access to class A anymore.

__init__ is used to initialize the object's state. Instead of calling methods that will initialize the object's members after it's created, __init__ solves this issue by initializing the object's members during creation time, so if you have a member called name inside a class and you want to initialize name when you create the class instead of calling an extra method init_name('name'), you would certainly use __init__ for this purpose.

So when I 'call' the class, I pass it the parameters from the __init__ method?

When you call the class, you pass the parameters (to) __init__ method?

Whatever arguments you pass the class, all the parameters will be passed to __init__ with one additional parameter added automatically for you which is the implied object usually called self (the instance itself) that will be passed always as the left-most argument by Python automatically:

class A:
def __init__(self, a, b):
self.a = a
self.b = b

        A(  34,  35) 
self.a = 34 | |
| |
| | self.b = 35
init(self, a, b)
|
|
|
The instance that you created by calling the class A()

Note: __init__ works for both classic classes and new style classes. Whereas, __new__ works only for new-style classes.

Why do we use __init__ in Python classes?

By what you wrote, you are missing a critical piece of understanding: the difference between a class and an object. __init__ doesn't initialize a class, it initializes an instance of a class or an object. Each dog has colour, but dogs as a class don't. Each dog has four or fewer feet, but the class of dogs doesn't. The class is a concept of an object. When you see Fido and Spot, you recognise their similarity, their doghood. That's the class.

When you say

class Dog:
def __init__(self, legs, colour):
self.legs = legs
self.colour = colour

fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")

You're saying, Fido is a brown dog with 4 legs while Spot is a bit of a cripple and is mostly yellow. The __init__ function is called a constructor, or initializer, and is automatically called when you create a new instance of a class. Within that function, the newly created object is assigned to the parameter self. The notation self.legs is an attribute called legs of the object in the variable self. Attributes are kind of like variables, but they describe the state of an object, or particular actions (functions) available to the object.

However, notice that you don't set colour for the doghood itself - it's an abstract concept. There are attributes that make sense on classes. For instance, population_size is one such - it doesn't make sense to count the Fido because Fido is always one. It does make sense to count dogs. Let us say there're 200 million dogs in the world. It's the property of the Dog class. Fido has nothing to do with the number 200 million, nor does Spot. It's called a "class attribute", as opposed to "instance attributes" that are colour or legs above.

Now, to something less canine and more programming-related. As I write below, class to add things is not sensible - what is it a class of? Classes in Python make up of collections of different data, that behave similarly. Class of dogs consists of Fido and Spot and 199999999998 other animals similar to them, all of them peeing on lampposts. What does the class for adding things consist of? By what data inherent to them do they differ? And what actions do they share?

However, numbers... those are more interesting subjects. Say, Integers. There's a lot of them, a lot more than dogs. I know that Python already has integers, but let's play dumb and "implement" them again (by cheating and using Python's integers).

So, Integers are a class. They have some data (value), and some behaviours ("add me to this other number"). Let's show this:

class MyInteger:
def __init__(self, newvalue):
# imagine self as an index card.
# under the heading of "value", we will write
# the contents of the variable newvalue.
self.value = newvalue
def add(self, other):
# when an integer wants to add itself to another integer,
# we'll take their values and add them together,
# then make a new integer with the result value.
return MyInteger(self.value + other.value)

three = MyInteger(3)
# three now contains an object of class MyInteger
# three.value is now 3
five = MyInteger(5)
# five now contains an object of class MyInteger
# five.value is now 5
eight = three.add(five)
# here, we invoked the three's behaviour of adding another integer
# now, eight.value is three.value + five.value = 3 + 5 = 8
print eight.value
# ==> 8

This is a bit fragile (we're assuming other will be a MyInteger), but we'll ignore now. In real code, we wouldn't; we'd test it to make sure, and maybe even coerce it ("you're not an integer? by golly, you have 10 nanoseconds to become one! 9... 8....")

We could even define fractions. Fractions also know how to add themselves.

class MyFraction:
def __init__(self, newnumerator, newdenominator):
self.numerator = newnumerator
self.denominator = newdenominator
# because every fraction is described by these two things
def add(self, other):
newdenominator = self.denominator * other.denominator
newnumerator = self.numerator * other.denominator + self.denominator * other.numerator
return MyFraction(newnumerator, newdenominator)

There's even more fractions than integers (not really, but computers don't know that). Let's make two:

half = MyFraction(1, 2)
third = MyFraction(1, 3)
five_sixths = half.add(third)
print five_sixths.numerator
# ==> 5
print five_sixths.denominator
# ==> 6

You're not actually declaring anything here. Attributes are like a new kind of variable. Normal variables only have one value. Let us say you write colour = "grey". You can't have another variable named colour that is "fuchsia" - not in the same place in the code.

Arrays solve that to a degree. If you say colour = ["grey", "fuchsia"], you have stacked two colours into the variable, but you distinguish them by their position (0, or 1, in this case).

Attributes are variables that are bound to an object. Like with arrays, we can have plenty colour variables, on different dogs. So, fido.colour is one variable, but spot.colour is another. The first one is bound to the object within the variable fido; the second, spot. Now, when you call Dog(4, "brown"), or three.add(five), there will always be an invisible parameter, which will be assigned to the dangling extra one at the front of the parameter list. It is conventionally called self, and will get the value of the object in front of the dot. Thus, within the Dog's __init__ (constructor), self will be whatever the new Dog will turn out to be; within MyInteger's add, self will be bound to the object in the variable three. Thus, three.value will be the same variable outside the add, as self.value within the add.

If I say the_mangy_one = fido, I will start referring to the object known as fido with yet another name. From now on, fido.colour is exactly the same variable as the_mangy_one.colour.

So, the things inside the __init__. You can think of them as noting things into the Dog's birth certificate. colour by itself is a random variable, could contain anything. fido.colour or self.colour is like a form field on the Dog's identity sheet; and __init__ is the clerk filling it out for the first time.

Any clearer?

EDIT: Expanding on the comment below:

You mean a list of objects, don't you?

First of all, fido is actually not an object. It is a variable, which is currently containing an object, just like when you say x = 5, x is a variable currently containing the number five. If you later change your mind, you can do fido = Cat(4, "pleasing") (as long as you've created a class Cat), and fido would from then on "contain" a cat object. If you do fido = x, it will then contain the number five, and not an animal object at all.

A class by itself doesn't know its instances unless you specifically write code to keep track of them. For instance:

class Cat:
census = [] #define census array

def __init__(self, legs, colour):
self.colour = colour
self.legs = legs
Cat.census.append(self)

Here, census is a class-level attribute of Cat class.

fluffy = Cat(4, "white")
spark = Cat(4, "fiery")
Cat.census
# ==> [<__main__.Cat instance at 0x108982cb0>, <__main__.Cat instance at 0x108982e18>]
# or something like that

Note that you won't get [fluffy, sparky]. Those are just variable names. If you want cats themselves to have names, you have to make a separate attribute for the name, and then override the __str__ method to return this name. This method's (i.e. class-bound function, just like add or __init__) purpose is to describe how to convert the object to a string, like when you print it out.

Why do we need __init__ to initialize a python class

Citation: But I can also do

class myClass():
x = 3
print("object created")

A = myClass()
print(A.x)
A.x = 6
print(A.x)

No you cannot. There is a fundamental difference once you want to create two or more objects of the same class. Maybe this behaviour becomes clearer like this

class MyClass:
x = 3
print("Created!")

a = MyClass() # Will output "Created!"
a = MyClass() # Will output nothing since the class already exists!

In principle you need __init__ in order to write that code that needs to get executed for every new object whenever this object gets initialized / created - not just once when the class is read in.

C++ equivalent of Python's __init__

The equivalent of Python's __init__ method in C++ is called a constructor. The role of both is to initialize/construct instance of class to be usable. There are some differences, though.

  • In Python, data members are initialized inside __init__, whereas in C++ they should be initialized using an initializer-list.

  • In C++, a constructor by default chains to the default constructor of the parent class, whereas in Python you have to explicitly call the parent __init__, preferably through super().

  • C++ allows function overloading, which extends to constructors. That allows to declare different constructors with different signatures (argument lists). To do the same in Python you have to declare __init__ as accepting *args,**kwargs and manually dispatch based on those.

  • In C++ there are spacial kinds of constructors, namely default constructor, copy constructor and move constructor.

Example

Python:

class Foo:
def __init__(self, x):
self.x = x
print "Foo ready"

C++:

class Foo {
public:
int x;
Foo(int x) : x(x)
{
std::cout << "Foo ready\n";
}
};

What is the difference between __init__ and __call__?

The first is used to initialise newly created object, and receives arguments used to do that:

class Foo:
def __init__(self, a, b, c):
# ...

x = Foo(1, 2, 3) # __init__

The second implements function call operator.

class Foo:
def __call__(self, a, b, c):
# ...

x = Foo()
x(1, 2, 3) # __call__

Why aren't superclass __init__ methods automatically invoked?

The crucial distinction between Python's __init__ and those other languages constructors is that __init__ is not a constructor: it's an initializer (the actual constructor (if any, but, see later;-) is __new__ and works completely differently again). While constructing all superclasses (and, no doubt, doing so "before" you continue constructing downwards) is obviously part of saying you're constructing a subclass's instance, that is clearly not the case for initializing, since there are many use cases in which superclasses' initialization needs to be skipped, altered, controlled -- happening, if at all, "in the middle" of the subclass initialization, and so forth.

Basically, super-class delegation of the initializer is not automatic in Python for exactly the same reasons such delegation is also not automatic for any other methods -- and note that those "other languages" don't do automatic super-class delegation for any other method either... just for the constructor (and if applicable, destructor), which, as I mentioned, is not what Python's __init__ is. (Behavior of __new__ is also quite peculiar, though really not directly related to your question, since __new__ is such a peculiar constructor that it doesn't actually necessarily need to construct anything -- could perfectly well return an existing instance, or even a non-instance... clearly Python offers you a lot more control of the mechanics than the "other languages" you have in mind, which also includes having no automatic delegation in __new__ itself!-).

How to return a value from __init__ in Python?

__init__ is required to return None. You cannot (or at least shouldn't) return something else.

Try making whatever you want to return an instance variable (or function).

>>> class Foo:
... def __init__(self):
... return 42
...
>>> foo = Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() should return None


Related Topics



Leave a reply



Submit