Chapter 14
In This Chapter
Defining the characteristics of a class
Specifying the class components
Creating your own class
Working with the class in an application
Working with subclasses
You’ve already worked with a number of classes in previous chapters. Many of the examples are easy to construct and use because they depend on the Python classes. Even though classes are briefly mentioned in previous chapters, those chapters largely ignore them simply because discussing them wasn’t immediately important.
Classes make working with Python code more convenient by helping to make your applications easy to read, understand, and use. You use classes to create containers for your code and data, so they stay together in one piece. Outsiders see your class as a black box — data goes in and results come out.
Besides helping you understand classes as a packaging method that avoids spaghetti code, this chapter helps you create and use your own classes for the first time. You gain insights into how Python classes work toward making your applications convenient to work with. This is an introductory sort of chapter, though, and you won’t become so involved in classes that your head begins to spin around on its own. This chapter is about making class development simple and manageable.
A class is essentially a method for packaging code. The idea is to simplify code reuse, make applications more reliable, and reduce the potential for security breaches. Well-designed classes are black boxes that accept certain inputs and provide specific outputs based on those inputs. In short, a class shouldn’t create any surprises for anyone and should have known (quantifiable) behaviors. How the class accomplishes its work is unimportant, and hiding the details of its inner workings is essential to good coding practice.
Before you move onto actual class theory, you need to know a few terms that are specific to classes. The following list defines terms that you need to know in order to use the material that follows later in the chapter. These terms are specific to Python. (Other languages may use different terms for the same techniques or define terms that Python uses in different ways.)
A class has a specific construction. Each part of a class performs a particular task that gives the class useful characteristics. Of course, the class begins with a container that is used to hold the entire class together, so that’s the part that the first section that follows discusses. The remaining sections describe the other parts of a class and help you understand how they contribute to the class as a whole.
A class need not be particularly complex. In fact, you can create just the container and one class element and call it a class. Of course, the resulting class won’t do much, but you can instantiate it (tell Python to build an object using your class as a blueprint) and work with it as you would any other class. The following steps help you understand the basics behind a class by creating the simplest class possible.
You see the familiar Python prompt.
class MyClass:
MyVar = 0
The first line defines the class container, which consists of the keyword class and the class name, which is MyClass. Every class you create must begin precisely this way. You must always include class followed by the class name.
The second line is the class suite. All the elements that comprise the class are called the class suite. In this case, you see a class variable named MyVar, which is set to a value of 0. Every instance of the class will have the same variable and start at the same value.
You have just created an instance of MyClass named MyInstance. Of course, you’ll want to verify that you really have created such an instance. Step 4 accomplishes that task.
The output of 0, as shown in Figure 14-1, demonstrates that MyInstance does indeed have a class variable named MyVar.
Python displays the class used to create this instance, as shown in Figure 14-2. The output tells you that this class is part of the __main__ module, which means that you typed it directly into the shell.
When you create a class, you can easily think that all you get is the class. However, Python adds built-in functionality to your class. For example, in the preceding section, you type __class__ and press Enter. The __class__ attribute is built in; you didn’t create it. It helps to know that Python provides this functionality so that you don’t have to add it. The functionality is needed often enough that every class should have it, so Python supplies it. The following steps help you work with the built-in class attributes.
If you haven’t followed the steps in the preceding section, “Creating the class definition,” please do so now.
A list of attributes appears, as shown in Figure 14-3. These attributes provide specific functionality for your class. They’re also common to every other class you create, so you can count on always having this functionality in the classes you create.
Python displays information on the __class__ attribute, as shown in Figure 14-4. You can use the same technique for learning more about any attribute that Python adds to your class.
Methods are simply another kind of function that reside in classes. You create and work with methods in precisely the same way that you do functions, except that methods are always associated with a class (you don’t see freestanding methods as you do functions). You can create two kinds of methods: those associated with the class itself and those associated with an instance of a class. It’s important to differentiate between the two. The following sections provide the details needed to work with both.
A class method is one that you execute directly from the class without creating an instance of the class. Sometimes you need to create methods that execute from the class, such as the functions you used with the str class in order to modify strings. As an example, the MultipleException4.py example in Chapter 9 uses the str.upper() function. The following steps demonstrate how to create and use a class method.
You see the familiar Python prompt.
class MyClass:
def SayHello():
print("Hello there!")
The example class contains a single defined attribute, SayHello(). This method doesn’t accept any arguments and doesn’t return any values. It simply prints a message as output. However, the method works just fine for demonstration purposes.
The example outputs the expected string, as shown in Figure 14-5. Notice that you didn’t need to create an instance of the class — the method is available immediately for use.
An instance method is one that is part of the individual instances. You use instance methods to manipulate the data that the class manages. As a consequence, you can’t use instance methods until you instantiate an object from the class.
The following steps demonstrate how to create and use instance methods in Python.
You see the familiar Python prompt.
class MyClass:
def SayHello(self):
print("Hello there!")
The example class contains a single defined attribute, SayHello(). This method doesn’t accept any special arguments and doesn’t return any values. It simply prints a message as output. However, the method works just fine for demonstration purposes.
Python creates an instance of MyClass named MyInstance.
You see the message shown in Figure 14-6.
A constructor is a special kind of method that Python calls when it instantiates an object using the definitions found in your class. Python relies on the constructor to perform tasks such as initializing (assigning values to) any instance variables that the object will need when it starts. Constructors can also verify that there are enough resources for the object and perform any other start-up task you can think of.
You see the familiar Python prompt.
class MyClass:
Greeting = ""
def __init__(self, Name="there"):
self.Greeting = Name + "!"
def SayHello(self):
print("Hello {0}".format(self.Greeting))
This example provides your first example of function overloading. In this case, there are two versions of __init__(). The first doesn’t require any special input because it uses the default value for the Name of "there". The second requires a name as an input. It sets Greeting to the value of this name, plus an exclamation mark. The SayHello() method is essentially the same as previous examples in this chapter.
Python doesn’t support true function overloading. Many strict adherents to strict Object-Oriented Programming (OOP) principles consider default values to be something different from function overloading. However, the use of default values obtains the same result, and it’s the only option that Python offers. In true function overloading, you see multiple copies of the same function, each of which could process the input differently.
Python creates an instance of MyClass named MyInstance.
You see the message shown in Figure 14-7. Notice that this message provides the default, generic greeting.
Python creates an instance of MyClass named MyInstance.
You see the message shown in Figure 14-8. Notice that this message provides a specific greeting.
As mentioned earlier in the book, variables are storage containers that hold data. When working with classes, you need to consider how the data is stored and managed. A class can include both class variables and instance variables. The class variables are defined as part of the class itself, while instance variables are defined as part of methods. The following sections show how to use both variable types.
Class variables provide global access to data that your class manipulates in some way. In most cases, you initialize global variables using the constructor to ensure that they contain a known good value. The following steps demonstrate how class variables work.
You see the familiar Python prompt.
class MyClass:
Greeting = ""
def SayHello(self):
print("Hello {0}".format(self.Greeting))
This is a version of the code found in the “Working with constructors” section of the chapter, but this version doesn’t include the constructor. Normally you do include a constructor to ensure that the class variable is initialized properly. However, this series of steps shows how class variables can go wrong.
This statement sets the value of Greeting to something other than the value that you used when you created the class. Of course, anyone could make this change. The big question is whether the change will take.
You see that the value of Greeting has changed, as shown in Figure 14-9.
Python creates an instance of MyClass named MyInstance.
You see the message shown in Figure 14-10. The change that you made to Greeting has carried over to the instance of the class. It’s true that the use of a class variable hasn’t really caused a problem in this example, but you can imagine what would happen in a real application if someone wanted to cause problems.
This is just a simple example of how class variables can go wrong. The two concepts you should take away from this example are as follows:
Instance variables are always defined as part of a method. The input arguments to a method are considered instance variables because they exist only when the method exists. Using instance variables is usually safer than using class variables because it’s easier to maintain control over them and to ensure that the caller is providing the correct input. The following steps show an example of using instance variables.
You see the familiar Python prompt.
class MyClass:
def DoAdd(self, Value1=0, Value2=0):
Sum = Value1 + Value2
print("The sum of {0} plus {1} is {2}."
.format(Value1, Value2, Sum))
In this case, you have three instance variables. The input arguments, Value1 and Value2, have default values of 0, so DoAdd() can’t fail simply because the user forgot to provide values. Of course, the user could always supply something other than numbers, so you should provide the appropriate checks as part of your code. The third instance variable is Sum, which is equal to Value1 + Value2. The code simply adds the two numbers together and displays the result.
Python creates an instance of MyClass named MyInstance.
You see the message shown in Figure 14-11. In this case, you see the sum of adding 1 and 4.
Sometimes you create methods that can take a variable number of arguments. Handling this sort of situation is something Python does well. Here are the two kinds of variable arguments that you can create:
You see an editor in which you can type the example code.
class MyClass:
def PrintList1(*args):
for Count, Item in enumerate(args):
print("{0}. {1}".format(Count, Item))
def PrintList2(**kwargs):
for Name, Value in kwargs.items():
print("{0} likes {1}".format(Name, Value))
MyClass.PrintList1("Red", "Blue", "Green")
MyClass.PrintList2(George="Red", Sue="Blue",
Zarah="Green")
For the purposes of this example, you’re seeing the arguments implemented as part of a class method. However, you can use them just as easily with an instance method.
Look carefully at PrintList1() and you see a new method of using a for loop to iterate through a list. In this case, the enumerate() function outputs both a count (the loop count) and the string that was passed to the function.
The PrintList2() function accepts a dictionary input. Just as with PrintList1(), this list can be any length. However, you must process the items() found in the dictionary to obtain the individual values.
You see the output shown in Figure 14-12. The individual lists can be of any length. In fact, in this situation, playing with the code to see what you can do with it is a good idea. For example, try mixing numbers and strings with the first list to see what happens. Try adding Boolean values as well. The point is that using this technique makes your methods incredibly flexible if all you want is a list of values as input.
In some situations, you want to be able to do something special as the result of using a standard operator such as add (+). In fact, sometimes Python doesn’t provide a default behavior for operators because it has no default to implement. No matter what the reason might be, overloading operators makes it possible to assign new functionality to existing operators so that they do what you want, rather than what Python intended. The following steps demonstrate how to overload an operator and use it as part of an application. This example also appears with the downloadable source code as OverloadOperator.py.
You see an editor in which you can type the example code.
class MyClass:
def __init__(self, *args):
self.Input = args
def __add__(self, Other):
Output = MyClass()
Output.Input = self.Input + Other.Input
return Output
def __str__(self):
Output = ""
for Item in self.Input:
Output += Item
Output += " "
return Output
Value1 = MyClass("Red", "Green", "Blue")
Value2 = MyClass("Yellow", "Purple", "Cyan")
Value3 = Value1 + Value2
print("{0} + {1} = {2}"
.format(Value1, Value2, Value3))
The example demonstrates a few different techniques. The constructor, __init__(), demonstrates a method for creating an instance variable attached to the self object. You can use this approach to create as many variables as needed to support the instance.
When you create your own classes, no + operator is defined until you define one, in most cases. The only exception is when you inherit from an existing class that already has the + operator defined (see the “Extending Classes to Make New Classes” section, later in this chapter, for details). In order to add two MyClass entries together, you must define the __add__() method, which equates to the + operator.
The code used for the __add__() method may look a little odd, too, but you need to think about it one line at a time. The code begins by creating a new object, Output, from MyClass. Nothing is added to Output at this point — it’s a blank object. The two objects that you want to add, self.Input and Other.Input, are actually tuples. (See “Working with Tuples,” in Chapter 13, for more details about tuples.) The code places the sum of these two objects into Output.Input. The __add__() method then returns the new combined object to the caller.
Of course, you may want to know why you can’t simply add the two inputs together as you would a number. The answer is that you’d end up with a tuple as an output, rather than a MyClass as an output. The type of the output would be changed, and that would also change any use of the resulting object.
To print MyClass properly, you also need to define a __str__() method. This method converts a MyClass object into a string. In this case, the output is a space-delimited string (in which each of the items in the string is separated from the other items by a space) containing each of the values found in self.Input. Of course, the class that you create can output any string that fully represents the object.
The main procedure creates two test objects, Value1 and Value2. It adds them together and places the result in Value3. The result is printed onscreen.
Figure 14-13 shows the result of adding the two objects together, converting them to strings, and then printing the result. It’s a lot of code for such a simple output statement, but the result definitely demonstrates that you can create classes that are self-contained and fully functional.
All the previous material in this chapter has helped prepare you for creating an interesting class of your own. In this case, you create a class that you place into an external module and eventually access within an application. Listing 14-1 shows the code that you need to create the class. This example also appears with the downloadable source code as MyClass.py.
Listing 14-1: Creating an External Class
class MyClass:
def __init__(self, Name="Sam", Age=32):
self.Name = Name
self.Age = Age
def GetName(self):
return self.Name
def SetName(self, Name):
self.Name = Name
def GetAge(self):
return self.Age
def SetAge(self, Age):
self.Age = Age
def __str__(self):
return "{0} is aged {1}.".format(self.Name,
self.Age)
In this case, the class begins by creating an object with two instance variables: Name and Age. If the user fails to provide these values, they default to Sam and 32.
As with just about every other class you create, you need to define the __str__() method if you want the user to be able to print the object. In this case, the class provides formatted output that lists both of the instance variables.
Most of the time, you use external classes when working with Python. It isn’t very often that a class exists within the confines of the application file because the application would become large and unmanageable. In addition, reusing the class code in another application would be difficult. The following steps help you use the MyClass class that you created in the previous section. This example also appears with the downloadable source code as MyClassTest.py.
You see an editor in which you can type the example code.
import MyClass
SamsRecord = MyClass.MyClass()
AmysRecord = MyClass.MyClass("Amy", 44)
print(SamsRecord.GetAge())
SamsRecord.SetAge(33)
print(AmysRecord.GetName())
AmysRecord.SetName("Aimee")
print(SamsRecord)
print(AmysRecord)
The example code begins by importing the MyClass module. The module name is the name of the file used to store the external code, not the name of the class. A single module can contain multiple classes, so always think of the module as being the actual file that is used to hold one or more classes that you need to use with your application.
After the module is imported, the application creates two MyClass objects. Notice that you use the module name first, followed by the class name. The first object, SamsRecord, uses the default settings. The second object, AmysRecord, relies on custom settings.
Sam has become a year old. After the application verifies that the age does need to be updated, it updates Sam’s age.
Somehow, HR spelled Aimee’s name wrong. It turns out that Amy is an incorrect spelling. Again, after the application verifies that the name is wrong, it makes a correction to AmysRecord. The final step is to print both records in their entirety.
The application displays a series of messages as it puts MyClass through its paces, as shown in Figure 14-14. At this point, you know all the essentials of creating great classes.
As you might imagine, creating a fully functional, production-grade class (one that is used in a real-world application actually running on a system that is accessed by users) is time consuming because real classes perform a lot of tasks. Fortunately, Python supports a feature called inheritance. By using inheritance, you can obtain the features you want from a parent class when creating a child class. Overriding the features that you don’t need and adding new features lets you create new classes relatively fast and with a lot less effort on your part. In addition, because the parent code is already tested, you don’t have to put quite as much effort into ensuring that your new class works as expected. The following sections show how to build and use classes that inherit from each other.
Parent classes are normally supersets of something. For example, you might create a parent class named Car and then create child classes of various car types around it. In this case, you build a parent class named Animal and use it to define a child class named Chicken. Of course, you can easily add other child classes after you have Animal in place, such as a Gorilla class. However, for this example, you build just the one parent and one child class, as shown in Listing 14-2. This example also appears with the downloadable source code as Animals.py.
Listing 14-2: Building a Parent and Child Class
class Animal:
def __init__(self, Name="", Age=0, Type=""):
self.Name = Name
self.Age = Age
self.Type = Type
def GetName(self):
return self.Name
def SetName(self, Name):
self.Name = Name
def GetAge(self):
return self.Age
def SetAge(self, Age):
self.Age = Age
def GetType(self):
return self.Type
def SetType(self, Type):
self.Type = Type
def __str__(self):
return "{0} is a {1} aged {2}".format(self.Name,
self.Type,
self.Age)
class Chicken(Animal):
def __init__(self, Name="", Age=0):
self.Name = Name
self.Age = Age
self.Type = "Chicken"
def SetType(self, Type):
print("Sorry, {0} will always be a {1}"
.format(self.Name, self.Type))
def MakeSound(self):
print("{0} says Cluck, Cluck, Cluck!".format(self.Name))
The Animal class tracks three characteristics: Name, Age, and Type. A production application would probably track more characteristics, but these characteristics do everything needed for this example. The code also includes the required accessors for each of the characteristics. The __str__() method completes the picture by printing a simple message stating the animal characteristics.
The Chicken class inherits from the Animal class. Notice the use of Animal in parentheses after the Chicken class name. This addition tells Python that Chicken is a kind of Animal, something that will inherit the characteristics of Animal.
Notice that the Chicken constructor accepts only Name and Age. The user doesn’t have to supply a Type value because you already know that it’s a chicken. This new constructor overrides the Animal constructor. The three attributes are still in place, but Type is supplied directly in the Chicken constructor.
Someone might try something funny, such as setting her chicken up as a gorilla. With this in mind, the Chicken class also overrides the SetType() setter. If someone tries to change the Chicken type, that user gets a message rather than the attempted change. Normally, you handle this sort of problem by using an exception, but the message works better for this example by making the coding technique clearer.
Finally, the Chicken class adds a new feature, MakeSound(). Whenever someone wants to hear the sound a chicken makes, he can call MakeSound() to at least see it printed on the screen.
Testing the Chicken class also tests the Animal class to some extent. Some functionality is different, but some classes aren’t really meant to be used. The Animal class is simply a parent for specific kinds of animals, such as Chicken. The following steps demonstrate the Chicken class so that you can see how inheritance works. This example also appears with the downloadable source code as ListStack.py.
You see an editor in which you can type the example code.
import Animals
MyChicken = Animals.Chicken("Sally", 2)
print(MyChicken)
MyChicken.SetAge(MyChicken.GetAge() + 1)
print(MyChicken)
MyChicken.SetType("Gorilla")
print(MyChicken)
MyChicken.MakeSound()
The first step is to import the Animals module. Remember that you always import the filename, not the class. The Animals.py file actually contains two classes in this case: Animal and Chicken.
The example creates a chicken, MyChicken, named Sally, who is age 2. It then starts to work with MyChicken in various ways. For example, Sally has a birthday, so the code updates Sally’s age by 1. Notice how the code combines the use of a setter, SetAge(), with a getter, GetAge(), to perform the task. After each change, the code displays the resulting object values for you. The final step is to let Sally say a few words.
You see each of the steps used to work with MyChicken, as shown in Figure 14-15. As you can see, using inheritance can greatly simplify the task of creating new classes when enough of the classes have commonality so that you can create a parent class that contains some amount of the code.
3.141.19.185