In our last tutorial we had our first look at classes and their attributes. The attributes of a class are the data which are stored in the class. The great thing about classes though is that we can use them to relate data to functions. Just as the data of a class are called attributes, the functions of a class have a special name as well. They are called “methods”. We have had a brief meeting with methodsearlier.

Here we’ve added Python’s “built in” range() function to the allDadsclass. Just as with attributes, when you create an instance of a class, the methods of the class are also inherited by the instance:

>>> myDad = allDads()
>>> myDad.r(6)
[0, 1, 2, 3, 4, 5]

Changing an instance’smethod does not change the class’s method:

>>> myDad.r = repr
>>> myDad.r(6) '6'

So, we have replaced the rfunction for the instancemyDad of the class allDads with Python’s “built in” reprfunction. We have briefly met repr before – what it does is (tries to) convert the object passed to it to a string so that you can print it. You can see in this example that the number 6 has been converted into the string ‘6’ (note the inverted commas).

Just for good measure, I’ll show that defining the method r doesn’t create a function of the same name:

You might not have noticed it, but we just covered one of the most powerful, mind blowing aspects of classes. It’s called “overriding”. Overriding occurs where an object which inherits from a class assigns a different method to the method the class has. We will return to overriding a little later, but make a note for now that it’s important. We don’t talk about overriding attributes, probably (I don’t actually know) because with attributes you’re just changing a value rather than substituting programming code which will be executed.

The r method of allDads is a bit silly. The rangemethod doesn’t have any meaning for allDads – nor, for that matter, does repr. You would not normally add methods to a class after it has been defined. It would be more normal to have all of the class’s methods set out as part of the definition of the class itself.

Init and a sense of self

There is a special method for classes (called __init__ [1]) which seems like it is silly (how do you __init__ a dad?), but it turns out to be very important. The __init__ method initialises an instance of the class. That is to say, whenever the class is instantiated, the __init__method runs (once). Because it is a method (function), arguments can be passed to it, so that the instance of the class can be customised.

However, in order for the class to customise itself at the time it is being instantiated, it needs some way of referring to the particular instance, rather than to the attributes of the class. So, in the previous tutorial, after we had created an instance, we could assign different values to the attributesinherited from the class by that instance. However, if __init__ is part of the class definition, it is written before any instances are made, so it doesn’t know about any instances. How does it refer to an instance it doesn’t know about – especially when there may be multiple instances of a class?

The answer is the “self” variable. Self refers to… itself. That is, when an instanceinherits from a class, and the class refers to self, the instance reads that reference as a reference to “myself the instance” not “the class’s self”. Using “self” is actually “only” a convention and it could be called something else (other languages use “my”). You should always use “self” and not some other name.

I printed dad1.age again to demonstrate that, when we initiatlised dad2, self referred to dad2, and had no impact on the instance dad1.

You need to get comfortable using self and __init__ because you will be using them a lot.

Exercise:

Rewrite the allDads class to add an attributeself.appearance, which is initialised by __init__(). Make appearance default to “Hairy”. Test it by making two instancesdad1 and dad2, passing an argument to appearance for dad1, but not for dad2. Print out dad1.appearance and dad2.appearance to confirm it worked properly.

Notes:

[1] Pronounced (among other ways) “dunder init” – see the Introspection post for some comments on pronunciation.