Exercise 13: Animal Wrangling

This week you'll be using a custom-designed object to coordinate animal care.

  • preserve.py - This may be completed in pairs if you wish.


A new nature preserve opening outside Madison, and you've been recruited by local environmentalists to help provide an estimate of its area requirements for lobbying purposes. They've given you a list of the animals which will live in the preserve, and you'll determine the area of land and volume of water that will be needed to house all of the animals.

Each animal in the preserve will be either a land OR water animal (not both). The tables below contain the amount of space necessary per animal for each type of animal.

Water Animals:

Species Vol (m^3)
Salmon 0.5
Trout 0.5
Catfish 0.3
Otter 15.0
River Snake 5.0

Land Animals:

Species Area (acre)
Deer 0.5
Rabbit 0.2
Black Bear 2.0
Gray Wolf 1.0
Moose 0.7

Note - this is a nature preserve, not a zoo! These animals will roam free, but we don't want them to be too crowded.

Program Requirements

Your program must:

  1. Prompt the user for a path to a file, and read in a list of animals from the file. A correctly-formatted input file, like animals.txt, will simply have one animal (species) name per line.
  2. Use the class definition we've provided for the animals. Each animal should be represented in your program as an instance of this class:

    class Animal:
        # Mappings from allowable species to their requirements
        water_dict = {'Salmon':.5,'Trout':.5,'Catfish':.3,'Otter':15,'River Snake':5}
        land_dict = {'Deer':.5,'Rabbit':.2,'Black Bear':2,'Gray Wolf':1,'Moose':.7}
        def __init__(self, name):
    	# Create an instance of the Animal class, including:
            #  - species         (the name of the animal)
            #  - domain          (land or water, or error if not a valid species)
            #  - area or volume  (from the reference above)      
            self.species = name
            if name in Animal.water_dict.keys():
                self.domain = 'water'
                self.vol = Animal.water_dict[name]
            elif name in Animal.land_dict.keys():
                self.domain = 'land'
                self.area = Animal.land_dict[name]
                self.domain = 'error'
        def get_requirement(self):
    	# Returns the animal's required space
            # Useful for general functions in your code!
            if self.domain == 'water':
                return self.vol
            elif self.domain == 'land':
                return self.area

    You'll have two methods, an __init__() that expects an animal's species, and a get_requirement() with no additional arguments that returns the number representing how much volume or area the animal needs. There are also two dictionaries of land and water animals, where the keys are the species and the values are the individual needs - you can access these using the syntax Animal.dictionary_name.
  3. Calculate the necessary space for the animals, one number for land animals and one for water animals. You should calculate two sets of numbers:
    1. Maximum: every animal gets its own space. This will be the number your environmentalists start negotiations at.
    2. Minimum: this is the largest total space when considering only one species at a time (for example, if you have 5 Trout and 1 Otter, you can fit the Trout's 2.5 m3 in the Otter's 15.0 m3, so the minimum area would be 15.0 m3). This is the number the environmentalists won't go below.
  4. BONUS: Graph the population sizes of each species of animal in a graph format of your choice, using matplotlib. This will be worth up to 3 bonus points.

Suggested Functions

The following functions (note: functions, not methods - you don't need to modify the provided class at all!) are not required. If you need some suggestions to help you get thinking, here are some that I implemented:

  • count_species(L, name) – returns the number of animals in L (a list of animals) with the species name.
  • total_species_area(L, name) – returns the total area/volume requirement of all members of species name in L.
  • total_area(L) – returns the total area/volume requirement of all animals in L.

Sample Output

The file does not exist:

Animal list: doesnotexist.txt
Error: the file doesnotexist.txt does not exist.

The file contains animals not in the list:

Animal list: badanimals.txt
Error: invalid animal Duck encountered in the file. Skipping.
Error: invalid animal Duck encountered in the file. Skipping.

Water Animals
Salmon: 0
Trout: 0
Catfish: 0
Otter: 0
River Snake: 0

Land Animals
Deer: 0
Rabbit: 0
Black Bear: 0
Gray Wolf: 0
Moose: 1

Maximum Area
Land: 0.7 acres
Water: 0 m^3

Minimum Area
Land: 0.7 acres
Water: 0 m^3

Using the sample input file (with random, implausible animal distributions):

Animal list: animals.txt

Water Animals
River Snake: 6
Otter: 9
Salmon: 16
Catfish: 10
Trout: 9

Land Animals
Gray Wolf: 7
Deer: 3
Moose: 2
Rabbit: 4
Black Bear: 4

Maximum Area
Land: 18.7 acres
Water: 180.5 m^3

Minimum Area
Land: 8 acres
Water: 135 m^3

Commenting your code

Once again, some of your program points will come from commenting your code.

  • Add a 1-2 sentence description of the program and its function to the top of your code.
  • Include comments within your code describing what you're doing. You don't need to comment on every line of code, but be clear in your explanations.

Submitting your files

As usual, you'll be handing in your lab work via the course Learn@UW dropboxes. Navigate to our 301 course page, and click the Dropbox link in the top navigation bar. You should see a dropbox for Program 13 - this is where you should hand in your preserve.py file.

Note that the dropbox will close at noon on 5 May, so be sure to submit your files before then.

Midterm review questions

This part of the assignment is ungraded, but we'll be talking about the questions in class on 4 May. I recommend working through the problems on your own first, but you may collaborate with as many people as you want (since this part is just for you and doesn't count toward your grade).


This is not intended to be similar to the length of a full exam, but questions are of comparable difficulty to the questions you will encounter on the exam.