Objects and Classes¶
Introduction¶
In these sheets, we’ve mostly used the word object to mean any thing Python can work with . So a number is an object; so is a string, or a list, or a dictionary.
But there’s another meaning of the word object. You may have heard the words object-oriented programming . This is a way of thinking about writing programs that’s particularly effective for large and complicated programs. Python lets you do object-oriented programming, and as it happens its objects are useful even in smaller programs.
This sheet is all about that kind of object in Python. For the Beginners’ course, it’s very optional. However, by the time you reach Sheet 5: The Robots Are Coming you’re going to want to start learning about Objects.
Objects and classes¶
In Python (and some other programming languages too) a kind of object is called a class, and, as you may have worked out by now, a thing which belongs to one of those kinds is called an object.
When you make a class, you can tell Python what the objects in that class can do. The class is like a mold for stamping out objects which do particular things.
The things that an object in that class can do are called methods. Methods are like functions which live inside an object (if you don’t know what a function is, now would be a good time to look at Sheet 2 or Sheet F).
Type the following into the Python window where you can see the >>> signs
(don’t type the >>> or ... parts, they’re just there to show you how
Python will respond to what you type).
>>> class Printer:
... def print_something(self, what):
... print('This object says', what)
... # Just press Enter here
You’ve just made a class called Printer. Objects belonging to this class
only know how to do one thing at the moment, as we’ve made one method called
print_something. Let’s make an object which belongs to the Printer
class, like this:
>>> printobj = Printer()
That was easy. Now printobj is an object from the Printer class.
Things in the Printer class know how to do print_something. Try this:
>>> printobj.print_something('Hello there')
The full stop is used to get at things which live inside a particular object,
so printobj.print_something uses the print_something method inside the
printobj object. The printobj object has the print_something method
because we made it from the Printer class.
Try getting printobj to say some other things. Try making some more objects
from the Printer class with different names, and get them to say things
too.
Variables in methods¶
Let’s try something else:
>>> class BetterPrinter: # define a class called BetterPrinter
... def set_print(self, what):
... self.thing_we_print = what
... def print_it(self):
... print(self.thing_we_print)
... # Just press Enter here
We’ve made another class called BetterPrinter. It can do a few interesting
things.
Tell Python to make an object which belongs to the BetterPrinter class.
Let’s call it bprinter, say. Hint: it works the same way as the thing we
typed to make an object belonging to Printer. Ask a neighbor or a team
member if you get stuck.
bprinter belongs to BetterPrinter, so it knows how to do set_print,
and also print_it, as these are the two methods we made for
BetterPrinter objects. Try this:
>>> bprinter.set_print('Hi there')
What do you think will happen when we tell bprinter to print_it? Try
it and see:
>>> bprinter.print_it()
Try changing what bprinter prints using the set_print method. You can
tell bprinter to print_it more than once without changing what you’ve
told it to print.
Try making some other objects belonging to the BetterPrinter class. If you
change what one object prints, does it change what another one prints? Try it.
When Python runs a method, it sets self to the object which the method
belongs to, so we can easily get at this object and the methods and variables
which live inside it. Anything else we put in brackets when we run the method
gets put into the variables we listed after self when we defined the
method. So when we say:
>>> bprinter.set_print('Hi there')
set_print runs with self set to the bprinter object and what
set to Hi there . (Take a look at where we defined the BetterPrinter class
above: you can see the names there).
Each object belonging to the BetterPrinter class stores what you tell it to
print in a variable called thing_we_print. Because we wanted each object to
have its own different thing_we_print, we had tell the object to use its own
thing_we_print when we use the set_print and print_it methods. This
is what self.thing_we_print means: all the objects we made from the
BetterPrinter class have a thing_we_print, so we have to say which one
we want. When we ran bprinter’s set_print method, self was set to
bprinter, so
self.thing_we_print = what # This is a line from the set_print method.
means
bprinter.thing_we_print = 'Hi there'
You can check this yourself, as Python doesn’t stop you having a look at each
object’s thing_we_print directly. Try this:
>>> print(bprinter.thing_we_print)
and you’ll see you’ve managed to get at bprinter’s thing_we_print
without using the print_it method. Try setting thing_we_print without
using the set_it method, and then using print_it to confirm you really
have changed what bprinter prints.
So, we can access and even change our objects’ variables directly rather than
through the set_print and print_it methods. Often it’s a bad idea to do
this from outside the class, though. Imagine you are writing a class for other
programmers to use: you want them to be able to use your class even if you
change how it works on the inside.
The way you can do this is to fix the method names, the parameters they take (those things you put in brackets when you call a function or method are called parameters, if you’d forgotten that), and what the method returns. These things, which the class presents for other people to use, are often called an interface (in ordinary English, an interface is just the place where two different things join, so this makes some kind of sense).
If the other programmers know that these things will stay the same whatever changes on the inside of the class, you can then do what you like inside the class to make it do what it’s supposed to do. You can even change the method if you think of a better way to do something: as long as we keep the interface fixed, the other people using your class won’t have to change the way their programs work.
But if people assume they can mess around with the insides of your class, if
you change those insides, their programs won’t work. Imagine we decide to
change the name of thing_we_print to thing because we don’t like typing
all those _ characters. A program which used thing_we_print instead of
going via set_print and print_it would not work once we’d done this.
But a program which used those two methods (and didn’t try to use
thing_we_print directly) would still work, as we’ve hidden the workings of
the class inside those two methods. This sort of thing becomes important when
you’re writing reasonably big programs or collaborating with other people on a
program.
Inheritance¶
Let’s try something else:
>>> class EvenBetterPrinter(BetterPrinter):
... def add_it(self, what):
... self.thing_we_print = self.thing_we_print + what
... # Just press Enter here.
You’ve just made a new class called EvenBetterPrinter. Tell Python to make
an object from that class called ebprinter. (It’s just like we’ve done
before: ask if you get stuck).
Now, try this:
>>> ebprinter.set_print('greetings earthlings')
>>> ebprinter.print_it()
What happened? You should find that both set_print and print_it are
methods which ebprinter has. But we didn’t define them when we defined
EvenBetterPrinter above, so what’s going on?
The answer is that when we created the EvenBetterPrinter class, we said
this:
class EvenBetterPrinter(BetterPrinter):
That tells Python that EvenBetterPrinter is a kind of BetterPrinter,
and can do everything that a BetterPrinter can do. So ebprinter knows
about set_print and print_it, because BetterPrinter defines them.
But there’s more to EvenBetterPrinter than what was in BetterPrinter.
Try this:
>>> ebprinter.add_it(', take me to your leader.')
>>> ebprinter.print_it()
What happened? Have a look back to the definition of the add_it method of
EvenBetterPrinter and see if you can work out what it does. Ask if you get
stuck.
Try making other objects from the EvenBetterPrinter class and playing with
the three methods these objects know about.
What about BetterPrinter? Do objects in that class now know about
add_it? Try making another object from BetterPrinter and see whether it
knows about add_it.
What we have done looks a bit like this (using rectangles for classes and ovals for methods):
EvenBetterPrinter is a subclass of BetterPrinter (on the picture
above, it’s underneath it: think of a submarine underneath the water, say). Or,
alternatively, BetterPrinter is a superclass of EvenBetterPrinter (I
suppose you could think of Superman, flying above it! Or maybe not).
We’ve seen that objects from EvenBetterPrinter have the methods that
BetterPrinter has, as well as the extra add_it method we gave
EvenBetterPrinter. The fancy name for this is inheritance, because the
EvenBetterPrinter class is like a child of the BetterPrinter
class and inherits these methods from it.
Try making other subclasses of BetterPrinter and making objects belonging
to the classes you’ve made. What happens if you make a class which inherits
from BetterPrinter but has its own print_it method which does something
different from BetterPrinter’s? (In fact, there’s a way to get at
BetterPrinter’s print_it even if you have overridden it in your
subclass, but we’ll not worry about that now).
What’s the point of all this? Well, as we said earlier, inheritance is a way to say that A is a kind of B. Maybe we’re writing a computer game where all the objects on the screen have some things in common: they all have a speed and a position, they respond to being hit by other objects, and so on. But they also have differences: the player’s ship can shoot bullets, for example, but the asteroids the player is shooting at cannot shoot back.
What we can do is make a class for an object on the screen which handles these common things, to save us having to write out the same methods over and over again. But all objects on the screen are not the same, so we can make subclasses of our object on the screen class to handle the things that each sort of object does differently. The player, the bullets and the asteroids are all kinds of objects on the screen , so they’re all subclasses of the object on the screen class.
Magic methods¶
There’s one more thing we need to know about how classes work in Python so that we can understand the worksheets which use them (the Games worksheets, for example).
Let’s make another subclass of our old friend BetterPrinter:
>>> class YAPrinter(BetterPrinter):
... def __init__ (self, what):
... self.thing_we_print = what
... print('An object from YAPrinter is born.')
... print("Yippee! I'm alive!")
... # Just press Enter here.
Try this:
>>> yap = YAPrinter("Bonjour")
What happened? This works because the method named __init__ is special in
Python ( init is short for initialisation, which is a fancy name for the
things we do to set something up for the first time). If a class defines an
__init__ method, that method is run whenever a new object from that class
is created. So when we made yap an object from the YAPrinter class, the
__init__ method ran and printed out its message.
You probably noticed something else which was different from what we’ve seen
before. When we created yap, we put something inside the brackets after the
name of the class. When we do this, the __init__ method gets passed what we
type in the variables we listed after self when we defined __init__.
So, inside __init__, what was set to Bonjour . Have a look at the
definition of __init__ above. What does it do with the what variable?
What will happen when you use yap’s print_it method? Talk about it to
your neighbor, and when you think you know, try it and see.
__init__ is useful because it allows us to set up the object with the
things it needs to know to work. For example, if we were writing our space
game, we might give the __init__ method the location on the screen where
each object starts the game.
There are other special methods in Python too, but you needn’t worry about
defining them accidentally as their names always begin with __ . As long as
you don’t begin any of your method names like that (unless you mean to) you’ll
be OK.
Conclusion¶
What have we learned? You should now know:
How to create a class and define methods for it.
How to create an object from that class and use its methods.
How to set variables which live inside objects.
How to create a subclass from another class.
How to use the
__init__method.
If you’re not sure about any of these things, go back to that section and play with creating objects and classes, and ask for help if you need it. Once you’ve got the hang of it, you can go on to use classes and objects in your own programs.