The final Critter Caretaker program combines parts of classes you've seen throughout this chapter. It also includes the menu system you've worked with that allows the user to interact with his or her very own critter.
The Critter class is the blueprint for the object that represents the user's critter. The class isn't complicated, and most of it should look quite familiar, but it's a long a enough piece of code that attacking it in pieces makes sense.
The constructor method of the class initializes the three public attributes of a Critter object: name, hunger, and boredom. Notice that hunger and boredom both have default values of 0, allowing a critter to start off in a very good mood.
# Critter Caretaker # A virtual pet to care for # Michael Dawson - 3/28/03 class Critter(object): """A virtual pet""" def __init__(self, name, hunger = 0, boredom = 0): self.name = name self.hunger = hunger self.boredom = boredom
I take the more relaxed posture of a Python programmer with this method and leave the attributes at their default public status. I plan to provide all the methods I suspect a client will need, which should encourage the client to interact with a Critter object only through those methods.
The __pass_time() method is a private method that increases a critter's hunger and boredom levels. It's invoked at the end of each method where the critter does something (eats, plays, or talks) to simulate the passage of time. I made this method private because it should only be invoked by another method of the class. I only see time passing for a critter when it does something (like eat, play, or talk).
def __pass_time(self): self.hunger += 1 self.boredom += 1
The mood property represents a critter's mood. The property is created from a single get method, __get_mood(), making it a read-only attribute. __get_mood() adds the values of a Critter object's hunger and boredom attributes. Based on the total, the method returns a string, either "happy", "okay", "frustrated", or "mad".
The interesting thing about the mood property is that it doesn't simply provide access to a private attribute. That's because the string that represents a critter's mood is not stored as part of the Critter object, it's calculated on the fly by __get_mood(). The mood property just passes on the string returned by __get_mood(). To client code, however, mood looks like any other read-only attribute of a Critter object created with a property.
def __get_mood(self): unhappiness = self.hunger + self.boredom if unhappiness < 5: mood = "happy" elif 5 <= unhappiness <= 10: mood = "okay" elif 11 <= unhappiness <= 15: mood = "frustrated" else: mood = "mad" return mood mood = property(__get_mood)
The talk() method announces the critter's mood to the world by accessing the Critter object's mood property. Then, the method invokes __pass_time().
def talk(self): print "I'm", self.name, "and I feel", self.mood, "now.\n" self.__pass_time()
The eat() method reduces the critter's hunger level by an amount passed to the parameter food. If no value is passed, food gets the default value of 4. The critter's hunger level is kept in check and not allowed to go below 0. Finally, the method invokes __pass_time().
def eat(self, food = 4): print "Brruppp. Thank you." self.hunger -= food if self.hunger < 0: self.hunger = 0 self.__pass_time()
The play() method reduces the critter's boredom level by an amount passed to the parameter fun. If no value is passed, fun gets the default value of 4. The critter's boredom level is kept in check and not allowed to go below 0. Finally, the method invokes __pass_time().
def play(self, fun = 4): print "Wheee!" self.boredom -= fun if self.boredom < 0: self.boredom = 0 self.__pass_time()
I put the main part of the program into its own function, main(). At the start of the program, I get the name of the critter from the user. Next, I instantiate a new Critter object. Since I don't supply values for hunger or boredom, the attributes start out at 0, and the critter begins life happy and content.
def main(): crit_name = raw_input("What do you want to name your critter?: ") crit = Critter(crit_name)
Next, I created the familiar menu system. If the user enters 0, the program ends. If the user enters 1, the object's talk() method is invoked. If the user enters 2, the object's eat() method is invoked. If the user enters 3, the object's play() method is invoked. If the user enters anything else, he or she is told the choice is invalid.
choice = None while choice != "0": print \ """ Critter Caretaker 0 - Quit 1 - Listen to your critter 2 - Feed your critter 3 - Play with your critter """ choice = raw_input("Choice: ") print # exit if choice == "0": print "Good-bye." # listen to your critter elif choice == "1": crit.talk() # feed your critter elif choice == "2": crit.eat() # play with your critter elif choice == "3": crit.play() # some unknown choice else: print "\nSorry, but", choice, "isn't a valid choice."
The next line of code calls the main() function and begins the program. The last line waits for the user before ending.
main() ("\n\nPress the enter key to exit.")