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.