Software Design Lecture Notes Fall 2004 For next time: 1) read Chapter 16 (which is the last chapter we will read!) 2) read the project document 3) work on Homework 6 Classes and methods ------------------- A method is a function defined inside a class. 1) The first parameter is always self 2) Methods are usually invoked on an object Try this: class Point: def method(self): print self.attr p = Point() p.attr = 'whatever' p.method() Now try this: print p.__dict__ Method is not really an attribute of the object print p.__class__ But every object knows what class it belongs to print p.__class__.method And classes contain methods. Try this: p.__class__.method(p) This is, in effect, what Python does when you invoke a method 1) find out what class the object is in 2) see if the class defines the method 3) if so, invoke the method, passing the object as the first parameter. So, p.method(x, y, z) gets transformed into Point.method(p, x, y, z) Which is why all methods take an object as the first parameter. Calling the first parameter self is a convention borrowed from some other OO programming languages. __init__ -------- init is, mostly, a normal method, with a couple of exceptions 1) you almost never invoke __init__ directly 2) normally, you invoke the constructor, like Terrapin(), and Python: a) creates a new Terrapin object b) passes it to __init__ as the first parameter 3) __init__ must return None, which is counterintuitive, because Terrapin() returns a reference to the new object. Homework 5 Solutions -------------------- 1) My solutions demonstrate a way of using objects to encapsulate the state of a process (an alternative to global variables). 2) Here's an idiom for appending onto a value in a dictionary: try: self.dict[self.prefix].append(word) except KeyError: self.dict[self.prefix] = [word] Notice the more specific except clause. Why can't we use the get idiom we saw before? self.dict[self.prefix] = self.dict.get(self.prefix, []).append(word) 3) in RandomText.random_text, notice a) the naming convention b) the use of recursion to handle a "start-over" condition 4) in main() notice the use of try to handle command-line args Homework 6 Hints ---------------- Is there a pattern that might be useful for finding the closest turtle? Any problems? Uses for try...except --------------------- Testing whether something is legal: # return 1 if a string is a legal integer def is_int(s): try: x = int(s) return 1 except: return 0 Handling an error in an application-specific way: def singular(x, y): try: res = x / y except: res = 10000.0 return res Checking values provided by a user and generating useful error messages. try: fp = open(filename, 'r') except: print 'The file %s doesn't seem to exist.' % filename sys.exit(1) Dealing with special cases without having to check first: for letter in word: try: dict[letter] += 1 # the entry already exists except KeyError: dict[letter] = 1 # special case: create a new entry Separating the normal case from error handling code: try: a = b/c lg = math.log(a) e = exp(lg * 300) return e except ZeroDivisonError: # c must be 0 except ValueError: # a must be zero or negative except OverflowError: # lg must be too big except: # catch-all for anything else In some cases, there is a performance advantage, because you don't have to perform a check before (or after) every operation. Performance Rule #1: Make the common case fast; make the rare case correct.