.. Copyright Gareth McCaughan, Paul Wright and Jeffrey Elkner. All rights reserved. CONDITIONS: A "Transparent" form of a document means a machine-readable form, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A form that is not Transparent is called "Opaque". Examples of Transparent formats include LaTeX source and plain text. Examples of Opaque formats include PDF and Postscript. Paper copies of a document are considered to be Opaque. Redistribution and use of this document in Transparent and Opaque forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of this document in Transparent form must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions of this document in Opaque form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution, and reproduce the above copyright notice in the Opaque document itself. - Neither the name of Scripture Union, nor LiveWires nor the names of its contributors may be used to endorse or promote products derived from this document without specific prior written permission. DISCLAIMER: THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, CONTRIBUTORS OR SCRIPTURE UNION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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 <5-robots.html>`__: *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 <2-tables.html>`__ 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). .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> bprinter.set_print('Hi there') What do you think will happen when we tell ``bprinter`` to ``print_it``? Try it and see: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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 .. sourcecode:: python self.thing_we_print = what # This is a line from the set_print method. means .. sourcecode:: python 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: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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: .. sourcecode:: python 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: .. sourcecode:: python >>> 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``: .. sourcecode:: python >>> 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: .. sourcecode:: python >>> 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.