Classy Methods, a Sense of Self (Classes Part 2)


Ratcatcher     Hello – Mr and Mrs Concrete?
Both     Yes.
Ratcatcher     Well, well, well, well, well, well, well, well, well, well, well, how very nice. Allow me to introduce myself. I am Leslie Ames, the Chairman of the Test Selection Committee, and I’m very pleased to be able to tell you that your flat has been chosen as the venue for the third test against the West Indies.
Mrs Concrete     Really?
Ratcatcher     No, it was just a little joke. Actually, I am the Council Ratcatcher.

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 methods earlier.

Let’s (re)create the class from the previous tutorial:

>>> class allDads(object):
 ...    pass
 ...

Now, let’s add a method to it:

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

Here we’ve added Python’s “built in” range() function to the allDads class.  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’s method does not change the class’s method:

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

So, we have replaced the r function for the instance myDad of the class allDads with Python’s “built in” repr function.   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:

>>> r(6)                                                                                                                                                                 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'r' is not defined

Overriding

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 range method 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 attributes inherited 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 instance inherits 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.

Let’s have an example:

>>> class allDads(object):                                                                                                                                           
...    def __init__(self,age=28):                                                                                                                             
...       self.age = age                                                                                                                                                 
...                                                                                                                                                                                                                                                                                                                             
>>> dad1 = allDads()
>>> dad1.age
28                                                                                                                                                                       
>>> dad2 = allDads(35)                                                                                                                                                   
>>> dad2.age
35                        
>>> dad1.age
28

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 attribute self.appearance, which is initialised by __init__().  Make appearance default to “Hairy”.  Test it by making two instances dad1 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.

3 Responses to Classy Methods, a Sense of Self (Classes Part 2)

  1. Pingback: Python 4 Kids: Classy Methods, a Sense of Self... | Python | Syngu

  2. Pingback: Classy Methods, a Sense of Self (Classes Part 2) | Code IT | Scoop.it

  3. Pingback: Links 18/10/2011: Rekonq 0.8, LibreOffice vs OOo | Techrights

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.