Boston Python workshop 2/Python classes: Difference between revisions
Content added Content deleted
imported>Jesstess No edit summary |
(Add link to python classes blog post) |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
Staff member [http://jhamrick.mit.edu |
Staff member [http://jhamrick.mit.edu Jessica Hamrick] wrote up an excellent document to motivate and explain Python classes. Read through it here or copy it into a file to run and experiment with it. |
||
This tutorial also exists in [http://jhamrick.mit.edu/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/ blog-post form]. |
|||
<pre> |
<pre> |
Latest revision as of 23:20, 8 July 2011
Staff member Jessica Hamrick wrote up an excellent document to motivate and explain Python classes. Read through it here or copy it into a file to run and experiment with it.
This tutorial also exists in blog-post form.
############################################# ## 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()