Revision as of 20:05, 18 May 2011 by imported>Jesstess(Created page with '<pre> ############################################# ## INTRODUCTION TO CLASSES AND INHERITANCE ## ############################################# # ---> For questions, feel free …')
#############################################
## INTRODUCTION TO CLASSES AND INHERITANCE ##
#############################################
# ---> For questions, feel free to email jhamrick@mit.edu <---
# NOTE: This file intentionally contains some statements that will
# generate errors. This is used to illustrate what things are and are
# not allowed. If you try to import this file it will throw an
# AttributeError, so don't be alarmed!
# Data structures like lists or strings are extremely useful, but
# sometimes they aren't enough to represent something you're trying to
# implement in Python. For example, let's say we needed to keep track
# of a bunch of pets. We could represent a pet using a list, for
# example, by specifying the first element of the list as the pet's
# name and the second element of the list as the pet's species. This
# is very arbitrary and nonintuitive, however -- how do you know which
# element is supposed to be which?
# Classes give us the ability to create more complicated data
# structures that contain arbitrary content. We can create a Pet
# class that keeps track of the name and species of the pet in
# usefully named attributes 'name' and 'species', respectively.
# Before we get into creating a class itself, we need to understand an
# important distinction. A class is something that just contains
# structure -- it defines how something should be laid out or
# structured, but doesn't actually fill in the content. For example,
# a Pet class may say that a Pet needs to have a name and a species,
# but it will not actually say what the pet's name or species is.
# This is where instances come in. An instance is a specific copy of
# the class that does contain all of the content. For example, if I
# create a pet, 'polly', with name Polly and species Parrot, then
# 'polly' is an instance of 'Pet'.
# Another way to think about this is to think about a form you need to
# fill out for the government. Let's say that there is a particular
# tax form that everybody has to fill out. Everybody fills out the
# same type of form, but the content that people put into the form
# differs from person to person. A class is like the form: it
# specifies what content should exist. Your copy of the form with
# your specific information is like an instance of a class: it
# specifies what the content actually is.
class Pet(object):
# When we create a new pet, we need to specify what it's name is
# and what it's species is. 'self' is the instance of the class.
# Remember that instances have the structure of the class but that
# the values within an instance may vary from instance to
# instance. So, we want to specify that our instance (self) has
# different values in it than some other possible instace. That
# is why we say 'self.name = name' instead of 'Pet.name = name'.
def __init__(self, name, species):
self.name = name
self.species = species
# We can also define methods to get the contents of the instance.
# This method, getName, takes an instance of a Pet as a parameter
# and looks up the pet's name. Again, we have to pass in the
# 'self' parameter so that the function knows which instance of
# Pet to operate on: it needs to be able to find out the content.
# You'll see below that when we call the getName function we don't
# actually pass anything to it: we just do polly.getName() or
# polly.getSpecies(). Why don't we have to pass in the 'self'
# parameter? This phenomena is a special behavior of Python: when
# you call a method on an instance, Python automatically figures
# out what 'self' should be (from the instance) and passes it to
# the function. An equivalent way of doing this would be:
# Pet.getName(polly)
# In this case, we don't do 'instance.getName()', so Python can't
# automatically figure out what 'self' should be. Instead, we
# have to manually pass in the instance so that getName can
# operate correctly.
def getName(self):
return self.name
# getSpecies is similar to getName.
def getSpecies(self):
return self.species
# This is a special function that is defined for all classes in
# Python. You can specify your own version of the function (known
# as "overriding" the function). By overriding the __str__
# function, we can define the behavior when we try to print the
# instance using the 'print' keyword. For example, if we try to
# print polly:
# >>> print polly
# Polly is a Parrot
def __str__(self):
return "%s is a %s" % (self.name, self.species)
# Let's create some pets!
polly = Pet("Polly", "Parrot")
ginger = Pet("Ginger", "Cat")
clifford = Pet("Clifford", "Dog")
print polly.getName() # prints "Polly"
print polly.getSpecies() # prints "Parrot"
print polly # prints "Polly is a Parrot"
print ginger.getName() # prints "Ginger"
print ginger.getSpecies() # prints "Cat"
print ginger # prints "Ginger is a Cat"
print clifford.getName() # prints "Clifford"
print clifford.getSpecies() # prints "Dog"
print clifford # prints "Clifford is a Dog"
# Sometimes just defining a single class (like Pet) is not enough.
# For example, some pets are dogs and most dogs like to chase cats.
# Birds are also pets but they generally don't like to chase cats. We
# can make another class that is Pet but is also specifically a Dog,
# for example: this gives us the structure from Pet but also any
# structure we specify for Dog.
class Dog(Pet):
# We want to specify that all Dogs have species "Dog", and also
# whether or not the dog like to chase cats. To do this, we need
# to define our own initialization function. We also need to call
# the parent class initialization function, though, because we
# still want the 'name' and 'species' fields to be initialized.
# If we did not have the 'Pet.__init__(self, name, "Dog")' line,
# then we could still call the methods getName and getSpecies.
# However, because Pet.__init__ was never called, the 'name' and
# 'species' fields were never created, so calling getName or
# getSpecies would throw an error.
def __init__(self, name, chases_cats):
Pet.__init__(self, name, "Dog")
self.chases_cats = chases_cats
def chasesCats(self):
return self.chases_cats
# And similarly for cats...
class Cat(Pet):
def __init__(self, name, hates_dogs):
Pet.__init__(self, name, "Cat")
self.hates_dogs = hates_dogs
def hatesDogs(self):
return self.hates_dogs
mister_pet = Pet("Mister", "Dog")
mister_dog = Dog("Mister", True)
# isinstance is a special function that checks to see if an instance
# is an instance of a certain type of class. Here we can see that
# mister_pet is an instance of Pet, but not Dog, while mister_dog is
# an instance of both Pet and Dog.
print isinstance(mister_pet, Pet) # prints True
print isinstance(mister_pet, Dog) # prints False
print isinstance(mister_dog, Pet) # prints True
print isinstance(mister_dog, Dog) # prints True
# Because mister_pet is a Pet, but not a Dog, we can't do this:
# AttributeError: 'Pet' object has no attribute 'chasesCats'
print mister_pet.chasesCats()
# because the Pet class has no chasesCats() method. We can, however,
# call chasesCats() on mister_dog, because it is defined for the Dog
# class:
print mister_dog.chasesCats() # prints True
# Now let's create some cats and dogs.
fido = Dog("Fido", True)
rover = Dog("Rover", False)
mittens = Cat("Mittens", True)
fluffy = Cat("Fluffy", False)
print fido # prints "Fido is a Dog"
print rover # prints "Rover is a Dog"
print mittens # prints "Mittens is a Cat"
print fluffy # prints "Fluffy is a Cat"
# prints "Fido chases cats: True"
print "%s chases cats: %s" % (fido.getName(), fido.chasesCats())
# prints "Rover chases cats: False"
print "%s chases cats: %s" % (rover.getName(), rover.chasesCats())
# prints "Mittens hates dogs: True"
print "%s hates dogs: %s" % (mittens.getName(), mittens.hatesDogs())
# prints "Fluffy hates dogs: False"
print "%s hates dogs: %s" % (fluffy.getName(), fluffy.hatesDogs())
# To make the difference between classes and instances a little bit
# clearer, we can try to call a method on a class instead of on an
# instance: it doesn't work! This is because these methods only know
# how to operate on an instance of the class, because only the
# instance contains the actual content.
# TypeError: unbound method getSpecies() must be called with Pet
# instance as first argument (got nothing instead)
print "All pets are %ss" % Pet.getSpecies()
# TypeError: unbound method getName() must be called with Dog instance
# as first argument (got nothing instead)
print "All dogs are named %s" % Dog.getName()
Cookies help us deliver our services. By using our services, you agree to our use of cookies.