
Beginning Python (2005)
.pdf
Classes and Objects
In fact, the method __len__ is how the len function works: It asks an object how long it is by asking this built-in method. This enables the designer of an object to define how the length is determined and to have the built-in function len behave correctly for any object that defines a __len__ method.
The other names beginning with an underscore also have special meanings. You can explore these in the Python shell. Pythoncard’s Python Shell will help you explore the normal methods of a string object, or any other method, by displaying possible names within the object that you are trying to call on, but it will not display internal names that begin with an underscore. You can determine those with the dir function yourself if you decide to do this.
Looking Ahead: How You Want to Use Objects
When you have an object, you want to be able to use it naturally. For instance, once you’ve defined it, the Omelet class could produce objects that behave in a way that would feel natural when you read the source code. You’re going to try to make something that can do this (you’ll see how to do this in the next section):
>>>o1 = Omelet()
>>>o1.show_kind() ‘cheese’
You’d also want to have a refrigerator that can be used as an object instead of just as a dictionary. It may be nice for you to be able to do things like be able to think of using it like a real fridge, whereby you can add food, remove food, check for foods, add or remove more than one thing at a time, and so on.
In other words, when you create an object that models something from the real world, you can form your program’s objects and classes so they help the pieces of the program work in a way that someone familiar with the real life object will recognize and be able to understand.
Defining a Class
When you are considering how even small programs of a few hundred lines of Python code is working, you will often realize that the program is keeping track of data in groups — when one thing is accessed, it affects other things that need to go along with it. Almost as often, you’ll realize that you’ve got whole lists of this interdependent data — lists in which the first element in list1 is matched to the first element in list2 and list3, and so on. Sometimes this can and should be solved by combining the lists creatively.
Python employs the concept of creating an entire class of code that acts as a placeholder. When a class is invoked, it creates an object bound to a name.
How Code Can Be Made into an Object
After you have an object bound to a name, using that name provides you with access to all of the data and functions you’ve defined. When you are writing code for a class, you start by declaring that class. This is done with the class keyword.
81
TEAM LinG

Chapter 6
Try It Out |
Defining a Class |
The definition of a class is simple and mainly involves the use of the special word class along with a name. The style is similar to the definition of a function, except that you do not follow a simple class definition with a tuple containing terms. (Doing that defines a class to inherit from, which you will see in Chapter 10.)
class Fridge:
“””This class implements a fridge where ingredients can be added and removed individually, or in groups.”””
How It Works
From here on out, everything indented will be available through the objects created inside of this class. You’ve already seen this with functions in Chapter 5, and similar rules apply to classes. Note that you have the option for the built-in docstring with classes, as you do with functions. They behave the same way and are very useful for providing an easy way to get information about the class.
You should try creating the Fridge class as shown in the preceding example. Note that a capital “F” was used for this. It’s a common convention for Python programmers to begin their class names with a capital letter; and when a class name has more than one word, it’s also common convention to run the words together, but to have each word begin with a capital letter to make it easier to read. For instance, a class that is modeling a fridge and a freezer together could be called FridgeAndFreezer.
Try It Out |
Creating an Object from Your Class |
Try typing the Fridge class into your ch6.py file (or a similar file for the examples here) and then invoke that file with python -i or the Run with Interpreter command, as you did in Chapter 5.
You can create a single object that is a Fridge by invoking it with the open and close parentheses:
>>> f = Fridge()
How It Works
At this point, you don’t have anything complicated defined yet. Fridge is basically empty, so this is your starting point. However, even without anything else, you should notice that you created an empty class that is usable. It does almost nothing, but there are situations in which you need very little. For instance, you can now treat this nearly empty object you’ve created like a special kind of dictionary. You can do this by adding names to your class interactively while you’re testing. This can help you develop an idea how you’d like it to work:
>>>f.items = {}
>>>f.items[“mystery meat”] = 1
In addition, as you’ll see demonstrated in Chapter 10, exceptions are actually classes, and sometimes all you need is an empty class to make an effective exception. You should only use this sort of direct access to a class when you have a simple, undefined class like this. When you have a more developed class, accessing the names inside of its scope can interfere with how the class was written, so it can cause a lot of trouble.
82 |
TEAM LinG |

Classes and Objects
The best way to start writing a class is to decide what you want it to do. For this, a Python-based model of refrigerator behaviors, Fridge, is the first thing, and it should be basic. While you’re thinking about it, focus on what you will need a particular Fridge object to do for your own purposes. You want enough behaviors available that this object can be used to make food, yet you don’t want to worry about aspects of real-life refrigerators that won’t be included in a simplified example, such as temperature, the freezer, defrosting, and electricity — all of these are unnecessary details that would only complicate our purpose here. For now, let’s just add to the docstring for the Fridge class to define the behaviors that you will be building soon.
First, you will want to have a way of stocking your Fridge. There are a couple of ways you’re going to do this: adding one type of a single item at a time and adding an entire dictionary at the same time so that it’s easy to initialize. Or simulating occasions when a refrigerator is filled, such as after you’ve come back from a shopping trip.
Second, you’ll want to have a way to take things out of the Fridge. You want to have the capability to do all of the same things when removing items as you do when you add: get a single item or get a whole bunch of things out of the Fridge.
You’ll want to write a couple of other things into this object to make this selective model of a Fridge: a function that will determine whether a particular item is available in the Fridge and another one that will check an entire dictionary worth of ingredients. These enable you to prepare to begin cooking.
These are all of the things that you would need to have in order to use a Fridge to store ingredients and to get them out when you want to cook but only for this limited purpose of modeling, of course. In other words, these will work as a model of this specific situation, while glossing over every possible scenario.
The methods that an object makes available for use are called its interface because these methods are how the program outside of the object makes use of the object. They’re what make the object useable.
The interface is everything you make available from the object. With Python, this usually means all of the methods and any other names that don’t begin with one or more underscores are your interfaces; however, it’s a good practice to distinguish which functions you expect to have called by explicitly stating what methods can be used, and how they’re used, in the class’s docstring:
class Fridge:
“””This class implements a fridge where ingredients can be added and removed individually, or in groups.
The fridge will retain a count of every ingredient added or removed, and will raise an error if a sufficient quantity of an ingredient isn’t present.
Methods:
has(food_name [, quantity]) - checks if the string food_name is in the fridge. Quantity will be set to 1 if you don’t specify a number.
has_various(foods) - checks if enough of every food in the dictionary is in the fridge
add_one(food_name) - adds a single food_name to the fridge add_many(food_dict) - adds a whole dictionary filled with food get_one(food_name) - takes out a single food_name from the fridge get_many(food_dict) - takes out a whole dictionary worth of food. get_ingredients(food) - If passed an object that has the __ingredients__
method, get_many will invoke this to get the list of ingredients.
“””
83
TEAM LinG

Chapter 6
def __init__(self, items={}):
“””Optionally pass in an initial dictionary of items””” if type(items) != type({}):
raise TypeError, “Fridge requires a dictionary but was given %s” %
type(items)
self.items = items return
In addition, documenting the methods you expect to be used is a good practice when you sit down to write a class — in effect, it is your outline for what you need to do to consider the class complete, and this can go hand-in-hand with testing your program as you write it. (See Chapter 12 for more about how to do this.)
When you write your interface methods, you’ll notice that, a lot of the time, simpler methods will share a lot of common features, like “get one thing” or “get two things” or “get some large number of things,” but to make them simple to call, you’ll want to keep all of these variations. At first, this will look seem as though it means that you need to duplicate a lot of the source code for each of these functions. However, instead of retyping the common components of your interface methods, you can save a lot of work by writing methods that are for internal use only.
These private methods can perform actions common to some or all of your interface methods. You’d want to do this when the private methods are more complex, or contain details that a user may not need to know in order to use them. By doing this, you can prevent confusion when your class is called, while making it easier for you to write. At its best, this is a clear win-win situation.
For the Fridge class, and in many classes you’ll write, it’s common to have a method that can operate on a group of data, and another method that works with just a single element. Whenever you have this situation, you can save your effort by making the method that works on a single item simply invoke the method that works on any number of items. In fact, sometimes it’s useful to have this method be considered private, or not a part of the interface. This way it can be used or not used and changed without affecting how the class is used, because any changes you make will not be seen outside an object, only inside.
For your Fridge class, you can minimize your work by creating an internal method called __add_multi that will take two parameters — the name of the item and the quantity of items — and have it add those to the items dictionary that each object has.
Try It Out |
Writing an Internal Method |
When you add this to your file for this chapter, remember to ensure that you have the right indentation for this to appear under your Fridge class, not alone at the top level. The class declaration is shown here to make this clear:
class Fridge:
#the docstring and intervening portions of the class would be here, and
#__add_multi should go afterwards.
def __add_multi(self, food_name, quantity): “””
__add_multi(food_name, quantity) - adds more than one of a food item. Returns the number of items added
84 |
TEAM LinG |

Classes and Objects
This should only be used internally, after the type checking has been done
“””
if not self.items.has_key(food_name): self.items[food_name] = 0
self.items[food_name] = self.items[food_name] + quantity
How It Works
Now you have a way of adding any number of single food items to a Fridge object. However, this is an internal method that doesn’t confirm whether the type that it is being given — either for food_name or quantity — is valid. You should use your interface functions to do this checking because, being a conscientious programmer, you will always ensure that you only pass the right values into your private methods. OK, just kidding. It’s always a good idea to check everywhere you can. For this example, you’re not going to check here, though, because you’re only going to use __add_multi in a foolproof way.
Now that you have the generally useful method __add_multi for your Fridge class, the add_one and the add_many methods can both be written to use it instead of your having to write similar functions two times. This will save you work.
Try It Out |
Writing Interface Methods |
To make this faster, you can avoid typing in the docstrings for now. They are here so that you understand better what the actual code is doing in case you have any questions.
Like before, these need to be indented beneath the Fridge class definition. Anything that seems to begin at the start of a line is actually a continuation from the line before and should all be entered on one line:
def add_one(self, food_name): “””
add_one(food_name) - adds a single food_name to the fridge returns True
Raises a TypeError if food_name is not a string. “””
if type(food_name) != type(“”):
raise TypeError, “add_one requires a string, given a %s” % type(food_name)
else:
self.__add_multi(food_name, 1)
return True
def add_many(self, food_dict): “””
add_many(food_dict) - adds a whole dictionary filled with food as keys and quantities as values.
returns a dictionary with the removed food.
raises a TypeError if food_dict is not a dictionary returns False if there is not enough food in the fridge. “””
85
TEAM LinG

Chapter 6
if type(food_dict) != type({}):
raise TypeError, “add_many requires a dictionary, got a %s” % food_dict
for item in food_dict.keys(): self.__add_multi(item, food_dict[item])
return
How It Works
add_one and add_many each serve similar purposes, and each one has the code to ensure that it is being used appropriately. At the same time, they both use __add_multi_to actually do the heavy lifting. Now if anything changes regarding how your class works inside of __add_multi, you will save time because it will change how both of these methods behave.
Now that you’ve written all of this, you have enough code written to put items into a Fridge object, but no way of taking items out. You can just directly access the object.items dictionary, but that is never a good idea except when testing. Of course, you’re testing now, so why not do that?
>>>f = Fridge({“eggs”:6, “milk”:4, “cheese”:3})
>>>f.items
{‘cheese’: 3, ‘eggs’: 6, ‘milk’: 4}
>>>f.add_one(“grape”)
True
>>>f.items
{‘cheese’: 3, ‘eggs’: 6, ‘grape’: 1, ‘milk’: 4}
>>>f.add_many({“mushroom”:5, “tomato”:3})
>>>f.items
{‘tomato’: 3, ‘cheese’: 3, ‘grape’: 1, ‘mushroom’: 5, ‘eggs’: 6, ‘milk’: 4}
>>>
So far, everything works! This is the simple part. The second thing you’ll want to add are the methods that enable you to determine whether something is in the Fridge.
It is important to write code that gives you a way to confirm that something is present because it can be used by the methods that remove items, get_one and get_many and get_ingredients, so that they ensure that they can check if enough of the items wanted are present. That’s exactly what the has and has_various methods are for:
def has(self, food_name, quantity=1): “””
has(food_name, [quantity]) - checks if the string food_name is in the fridge. Quantity defaults to 1
Returns True if there is enough, False otherwise. “””
return self.has_various({food_name:quantity})
def has_various(self, foods): “””
has_various(foods) determines if the dictionary food_name has enough of every element to satisfy a request.
returns True if there’s enough, False if there’s not or if an element does
86 |
TEAM LinG |

Classes and Objects
not exist. “””
try:
for food in foods.keys():
if self.items[food] < foods[food]: return False
return True except KeyError: return False
After has and has_various are in place, you can use a Fridge object in tests, and when you read the code, it will almost make sense when you read your code out loud.
Try It Out |
Using More Methods |
You can now invoke your ch6.py file with python -i or the Run with Interpreter command so that you can use everything you’ve added to the Fridge class. If you get errors instead of the >>> prompt, pay attention to the exception raised and try to fix any indentation, spelling, or other basic errors identified.
The class should be usable like this now:
>>>f = Fridge({“eggs”:6, “milk”:4, “cheese”:3})
>>>if f.has(“cheese”, 2):
... |
print “Its time to make an omelet!” |
... |
|
Its time to make an omelet!
How It Works
Now that you’ve defined new methods, the f object can use them. When you re-created f with the eggs, milk, and cheese you made the object out of the new Fridge class, so it has the new methods you’ve added available to it.
Finally, it’s time for the methods to get items from the Fridge. Here you can do the same thing you did for the methods to add to the Fridge, focusing on a single method that will take care of the hard stuff and letting the interface methods rely on this hard-working guy:
def __get_multi(self, food_name, quantity): “””
_get_multi(food_name, quantity) - removes more than one of a food item. Returns the number of items removed
returns False if there isn’t enough food_name in the fridge.
This should only be used internally, after the type checking has been done
“””
try:
if not self.has(food_name, quantity): return False
self.items[food_name] = self.items[food_name] - quantity
87
TEAM LinG

Chapter 6
except KeyError: return False return quantity
After this has been defined, you can create the remaining methods that the Fridge class’s docstring has specified. They each use __get_multi so that they can remove items from the Fridge with a minimal amount of extra coding on your part:
def get_one(self, food_name): “””
get_one(food_name) - takes out a single food_name from the fridge
returns a dictionary with the food:1 as a result, or False if there wasn’t enough in the fridge.
“””
if type(food_name) != type(“”):
raise TypeError, “get_one requires a string, given a %s” % type(food_name)
else:
result = self.__get_multi(food_name, 1) return result
def get_many(self, food_dict): “””
get_many(food_dict) - takes out a whole dictionary worth of food. returns a dictionary with all of the ingredients
returns False if there are not enough ingredients or if a dictionary isn’t provided.
“””
if self.has_various(food_dict): foods_removed = {}
for item in food_dict.keys():
foods_removed[item] = self.__get_multi(item, food_dict[item]) return foods_removed
def get_ingredients(self, food): “””
get_ingredients(food) - If passed an object that has the __ingredients__ method, get_many will invoke this to get the list of ingredients.
“””
try:
ingredients = self.get_many(food.__ingredients__()) except AttributeError:
return False
if ingredients != False: return ingredients
You’ve now written a completely usable class for a refrigerator. Remember that there are many directions in which you can take this. Although you may be making omelets that use the Fridge class now, you can also use it for other projects — to model the product flow of a business, for example, such as a deli that has ten refrigerators with different products in each one.
88 |
TEAM LinG |

Classes and Objects
When you do find an opportunity to repurpose a class that you’ve written (or a class that you’ve used), you can take advantage of the opportunity that is presented by adding features to support new needs without sacrificing what it already does.
For instance, an application that needs to take into account several refrigerators may result in a need for each Fridge object to have extra attributes, such as a name for it (like “dairy fridge”), its position in the store, its preferred temperature setting, and its dimensions. You can add these to the class, along with methods to get and set these values, while still keeping it completely usable for the omelet examples in this book. This is how interfaces help you. As long as the interfaces to the Fridge class you’ve already written here aren’t changed, or at least as long as they behave the same, you can otherwise modify anything. This capability to keep interfaces behaving the same is what is called their stability.
Objects and Their Scope
As you saw in Chapter 5, functions create their own space, a scope, for the names that they use. While the function is being invoked, the name and value are present, and any changes made to the name persist for as long as the function is in use. However, after the function has finished running and is invoked again, any work that was done in any prior invocations is lost, and the function has to start again.
With objects, the values inside of them can be stored and attached to self on the inside of the object (self in this case is a name that refers to the object itself, and it’s also the same as what is referenced by a name on the outside of the object, such as f). As long as the object is referenced by a name that is still active,
all of the values contained in it will be available as well. If an object is created in a function and isn’t returned by that function to be referenced to a name in a longer-lived scope, it will be available for as long as the single invocation of the function in which it was called, in the same way as any other data in the function.
Multiple objects are often created in tandem so that they can be used together. For instance, now that you’ve implemented all of the features you need to have a workable Fridge in your program, you need to have an Omelet object that works with it.
Try It Out |
Creating Another Class |
You’ve already created a class — a Fridge. Using the same format, create an Omelet class that you can use:
class Omelet:
“””This class creates an omelet object. An omelet can be in one of two states: ingredients, or cooked.
An omelet object has the following interfaces: get_kind() - returns a string with the type of omelet set_kind(kind) - sets the omelet to be the type named
set_new_kind(kind, ingredients) - lets you create an omelet
mix() - gets called after all the ingredients are gathered from the fridge cook() - cooks the omelet
“””
def __init__(self, kind=”cheese”): “””__init__(self, kind=”cheese”)
This initializes the Omelet class to default to a cheese omelet. Other methods
89
TEAM LinG

Chapter 6
“”” self.set_kind(kind) return
How It Works
You’ve now got a class whose intent is clearly spelled out. You’ve seen most of these behaviors in functions that you saw in Chapter 5, but now you have a structure within which you can combine all of these behaviors.
This class will have interface methods that enable the omelet to use a Fridge object cooperatively, and it will still offer the capability to create customized omelets as it could in Chapter 5.
Remember that all of the following code has to be indented one level beneath the Omelet class to be used:
def __ingredients__(self):
“””Internal method to be called on by a fridge or other objects that need to act on ingredients.
“””
return self.needed_ingredients
def get_kind(self): return self.kind
def set_kind(self, kind):
possible_ingredients = self.__known_kinds(kind) if possible_ingredients == False:
return False else:
self.kind = kind
self.needed_ingredients = possible_ingredients
def set_new_kind(self, name, ingredients): self.kind = name self.needed_ingredients = ingredients return
def __known_kinds(self, kind): if kind == “cheese”:
return {“eggs”:2, “milk”:1, “cheese”:1} elif kind == “mushroom”:
return {“eggs”:2, “milk”:1, “cheese”:1, “mushroom”:2} elif kind == “onion”:
return {“eggs”:2, “milk”:1, “cheese”:1, “onion”:1} else:
return False
def get_ingredients(self, fridge): self.from_fridge = fridge.get_ingredients(self)
def mix(self):
for ingredient in self.from_fridge.keys():
90 |
TEAM LinG |