ford
ford

Reputation: 180

Creating objects with classes, why do I need __init__(self, args):?

Sorry for posting such strange question, usually when I am confused I just "go with it" until I have an epiphany, and later sort it out, but calling def __init__(self): inside my class seems totally redundant and seems that leaving it out works just as well:

class Pet(object):
    type_of_pet = ""
    number_of_legs = 0
    name = ""

Lets me create:

fred = pet()
fred.type_of_pet = "alligator"

Just the same as if I were to change the class to:

class Pet(object):
    def __init__(self):
         type_of_pet = ""

alice = Pet()
alice.type_of_pet = "Pterodactyl" 

I'm just not getting why I need __init__ and the other threads on here were not as clear as I had hoped. Is there anything I can do to make this "click"?

Upvotes: 2

Views: 2294

Answers (5)

kindall
kindall

Reputation: 184081

You're asking why you need __init__() when you can just assign attributes on the class after it's instantiated.

And the answer is, so you can pass the attribute values when instantiating the object, like you're supposed to. This doesn't just save you typing; assigning attributes on the class after instantiation is prone to error: what if you accidentally assigned to number_off_legs instead of number_of_legs? This is not itself an error because attributes may be named anything, but it will cause other code (that expects number_of_legs) to fail or, in your case, to get the wrong data (since the default value on the class would be used at this point). Also, if you need to change an attribute name, that name is all over your code so you'd need to change it in a lot of places.

So to avoid these headaches, you want to write a little function that creates the object and then assigns the attributes on it, and performs any other necessary initialization (opening files, creating subordinate objects, and so on) before giving the object back to you. That's your __init__() method and Python helpfully attaches it to your class so that you can instantiate the object and pass in the attribute values all at once. It doesn't seem very useful when you're just storing some data, but when your classes get more complicated you will find it very convenient.

Upvotes: 1

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

__init__ is called on each instance of your class immediately after creating it. It's the correct place to do instance-level initialization.

Your type_of_pet and other attributes defined in your first example are class-level information. That form declares that the Pet class itself has a type_of_pet value, and that value exists at the same level as, say, any methods you might define.

When you check an instance for a name, it will get the value assigned to that instance if there is one, or go to the class level if there isn't. So this:

fred = Pet()
print fred.type_of_pet

actually checks type_of_pet on the class itself.

Assignment will modify only the object you actually call it on, so this sets a value at the instance level:

fred.type_of_pet = 'alligator'

That makes no change to the class, just puts a value for the name type_of_pet into the fred instance. Therefore, it's common to use class-level definitions as defaults for instances.

But where this will get you in trouble is with mutable types like list. This will not do what you expect:

class Pet:
    vaccinations = []

fido = Pet()
fido.vaccinations.append('rabies')

Because fido.vaccinations ends up being a reference to the single list defined at the class level. Instead, you'd use __init__ to give each pet its own independent list.

Personally, I'm increasingly of the belief that using class-level attributes as defaults for nonmutable types creates more confusion than it's worth, and should be saved only for things that truly should be tracked at the class level rather than per instance. But that's opinion.

Upvotes: 2

Vaughn Cato
Vaughn Cato

Reputation: 64308

When you define the members inside the class directly, those members become part of the class itself, not part of the instances of the class. You can still access them through the instances, but each instance doesn't have its own.

Upvotes: 0

tobspr
tobspr

Reputation: 8376

__init__ is a kind of constructor, when a instance of a class is created, python calls __init__() during the instantiation to define additional behavior that should occur when a class is instantiated, basically setting up some beginning values for that object or running a routine required on instantiation.

(source)

You could for example change your Pet class to:

class Pet(object):
    def __init__(self, type_of_pet):
         self.type_of_pet = type_of_pet

And make a new instance with:

alice = Pet("Pterodactyl")

If you initialize your class variables outside of the __init__ function, every instance will share that values. This is usefull for static classes, but I don't think you want that behaviour on "normal" classes.

Btw, you cannot use type_of_pet="" in __init__, it has to be self.type_of_pet="".

Upvotes: 1

user2357112
user2357112

Reputation: 280207

Putting assignment statements in a class definition doesn't declare instance attributes; it sets class attributes. If you do that, all pets will share the same name, type_of_pet, and number_of_legs.

__init__ is, among other things, used to set instance attributes:

def __init__(self, type_of_pet, name, number_of_legs):
    self.type_of_pet = type_of_pet
    self.name = name
    self.number_of_legs = number_of_legs

This will set attributes on the instance, rather than the class, so each object will have its own name, type, and leg count.

Upvotes: 6

Related Questions