-3

Hey guys so I have done a lot of research into _init_() but still don't get it. From what I currently understand it must be used for all instances of a class to pass object args?

I understand the use of self but for whatever reason I can not seem to wrap my head around the point of _init_(). Why do we have to use it. When is it useful and what purpose does it serve other than the instantiation of instances of a class?

How is it a constructor if the said object has already been made before the call to _init_()?

Generally speaking I just have no idea on the why, how or when behind _init_() other than it is used for instantiation of instances of a class, it can have multiple args which seem to be specific to the instance of the class. It must be called for each instance?

martineau
  • 119,623
  • 25
  • 170
  • 301
Aeryes
  • 629
  • 3
  • 10
  • 24
  • 1
    It's purpose is to initialize values that belong to this particular instance of the class. You don't have to define one, but in that case the instances created will have an empty initial state—which doesn't sound very useful. What part of the [documentation](https://docs.python.org/3/reference/datamodel.html#basic-customization) was unclear? – martineau Dec 20 '17 at 18:38
  • So what purpose does that serve? Can these values be used elsewhere ? – Aeryes Dec 20 '17 at 18:40
  • I'd suggest you look at some Python code that uses classes and see how `__init__` is being used. It should become clear then what its purpose is. For example, take a look at the [`Request` class](https://github.com/requests/requests/blob/master/requests/models.py#L194) from the popular `requests` library. You can see how each instance is _configured_ with certain values and then those values are used from other methods. –  Dec 20 '17 at 18:41
  • Say you had some class `Car`. All cars have a color. To make a new `Car` object `c` such that `c.color` is `"red"`, you would have `def __init__(self, color): self.color = color`. Then after `c = Car('red')`, `c` would be a `Car` object such that `c.color == 'red'` – Patrick Haugh Dec 20 '17 at 18:42
  • Aeryes: You can't use them until they are added somehow. For example, after creating an instance of a class by calling it: `inst = Class()`, you can add attributes by assigning them to it `inst.ans = 42`. You'll need to do this every time you create one (if you want them all to have certain values). – martineau Dec 20 '17 at 18:42
  • @Aeryes please check my answer anyway –  Dec 20 '17 at 19:01
  • It's `__init__`, not `_init_`. – Karl Knechtel Sep 05 '22 at 06:55

2 Answers2

3

I am not an expert on this but AFAIK the __init__ method is a built-in method of every class or metaclass, that is called exactly in the class instantiation.

Why or what for would you use the __init__ method?

Well, you can use it for many things, but the main purpose is to pass arguments into the class instance when it is instantiated. Actually when you do class(*args, **kwargs) these get passed along to __init__, where you might either make use of them or not.

For example:

class Vehicle:

    name = 'vehicle'
    price = None
    canMove = False
    position = 0

    def __init__(self, price, name=None, canMove=False):
        self.name = name if name else self.name
        self.price = price

    def move(self, distance):
        if self.canMove:
            self.position += distance
            return 'The vehicle moved {0} meters'.format(distance)
        return 'The vehicle cannot move'


class Car(Vehicle):
    name = 'car'
    consumption = 100 # (litres per meter)
    fuel = 0 # (litres )
    
    def __init__(self, fuel, consumption=None, *args, **kwargs):
        self.fuel = fuel
        self.consumption = consumption if consumption else self.consumption
        super().__init__(*args, **kwargs)

    def move(self, distance):
        if self.canMove and self.hasFuel():
            available_d = self.fuel / self.consumption
            if distance <= available_d:
                self.fuel -= self.consumption*distance
                self.position += distance
                return 'The vehicle moved {0} meters and has {1} litres left.'.format(distance, self.fuel)
            return 'The vehicle cannot move since it does not have fuel enough'
        return 'The vehicle cannot move since it does not have any fuel'


    def hasFuel(self):
        return True if self.fuel > 0 else False
    
    def giveFuel(self, litres):
        self.fuel += litres

If you take some time to read the code, you will see how there are necessary tasks that need to be run in the __init__ method, such as variable assignment, doing checks or running other processes.

Also when you inherit from another class the behaviour gets a bit more complicated. As you see I have to call the mother class with the super().__init__() call in order to run the rest of the tasks that were being done in it.

__init__ in use

So let's play around with this piece of code I have just created.

You could create your vehicle just like this:

myVehicle = Vehicle()

However that would raise an error, since the __init__ method is requiring to pass one compulsory argument, price. All the rest are optional fields, but that is obligatory for the instantiation to happen. Therefore you could try again with this:

myVehicle = Vehicle(10000)

You can access the value you have just passed doing:

myVehicle.price

You can also pass the rest of the elements, but they are not obligatory:

myVehicle2 = Vehicle(10000, name='myVehicle2', canMove=True)

Bear in mind that we could also assign them after the instantiation to our first vehicle:

myVehicle.name = 'myVehicle'

So now we have two different vehicles, but one can move and the other can't. We can see this if we run:

myVehicle.move(100)

Whereas myVehicle2.move(100) won't raise the error. If you change the property of the first vehicle afterwards it will work, regardless of the value you passed to __init__ initially.

It works similar for our Car class. If we do Car():

>>> Car()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'fuel'

Even if we do Car(fuel=140):

>>> Car(fuel=140)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 10, in __init__
TypeError: __init__() missing 1 required positional argument: 'price'

All these arguments are defined in the __init__ section, and the price tag is required because it inherits form the parent class.

Look at how the values work correctly when we try to move our car:

>>> myCar = Car(price=10000, fuel=150, consumption=10)
>>> myCar.move(10)
'The vehicle cannot move since it does not have any fuel' # we actually have not enabled its canMove property
>>> myCar.canMove = True
>>> myCar.move(10)
'The vehicle moved 10 meters and has 50 litres left.'
>>> myCar.move(1)
'The vehicle moved 1 meters and has 40 litres left.'
>>> myCar.move(30)
'The vehicle cannot move since it does not have fuel enough'
Community
  • 1
  • 1
  • Love this answer. Makes things a lot clearer. I think tonight when I get home I will mess around with this code and learn for myself. Thanks so much !!! – Aeryes Dec 20 '17 at 19:07
  • @Aeryes please if you like it consider upvoting and accepting it with the check button. Also feel free to ask any further question if you'd like. –  Dec 20 '17 at 19:09
  • I do have one question about the call to super. What args are been used in this instance. Also I gladly upvoted and will accept it. – Aeryes Dec 20 '17 at 19:12
  • So yeah, super what it does is to call the mother class. So you must admit all those `*args, **kwargs` and pass them to the parent `__init__` method unless you want to overwrite it completely. `*args, **kwargs` are, for short, a reduced way of expressing all the possible arguments that the class instantiation might accept (and in general any method or function). So if I did not do that, I would not be able to set on instantiation the price of the vehicle (I could still do it later). –  Dec 20 '17 at 19:16
  • I see so basically you are using the args that you provided in the mother class of 'Vechile' so the class 'Car inherits these and can use them in its own way? – Aeryes Dec 20 '17 at 19:22
0

It gets called when that type of object gets created, so you can do whatever class-specific initialization is needed, and don't have to remember to make a separate initialization call. That is what it is used for; that is what purpose it serves.

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
  • So what kind of class initialization would need to be done? If I assign args to the instance can they be used outside of the instance. Like assigning an instance of a class an arg of x = 2. Can I use x outside of the _init_. – Aeryes Dec 20 '17 at 18:38
  • 3
    In the "lot of research" you have done, you have never seen an example of how the args to `__init__` get used? – Scott Hunter Dec 20 '17 at 18:41
  • 1
    I know it takes some time to write a good, clear and simple answer about this topic, but you might agree with me that yours is none of those. –  Dec 20 '17 at 18:52
  • In all honesty I am just trying to learn, the hostility isn't needed. Yes I have but I am trying to get better examples from people who have been doing this for some time. Sorry for trying to learn. – Aeryes Dec 20 '17 at 19:06