Software Design Spring 2008 For today you should have: 1) read the handout from Domain-Driven Design http://wb/sd/handouts/evans_ch01.pdf 2) Read the Wikipedia page on the Observer pattern 3) Registered for Computational Modeling (well, not yet) Today 1) Domain-driven design 2) Special methods 3) Short circuit evaluation and "Live Free or Die" 4) Demos For next time you should: 1) prepare for a quiz on UML object diagrams Special methods --------------- One of the ways to customize the behavior of Python objects is by defining "special methods" You have already seen: __init__ which is invoked when you construct an object __str__ which is invoked when you print an object __cmp__ which is invoked when you use any of the relative operators Another example is __call__ which is invoked when you use an object as a function class Callable: """this class is used to wrap a function and its arguments into an object that can be passed as a callback parameter and invoked later. It is from the Python Cookbook 9.1, page 302 """ def __init__(self, func, *args, **kwds): self.func = func self.args = args self.kwds = kwds def __call__(self): return apply(self.func, self.args, self.kwds) def __str__(self): return self.func.__name__ Example: callable = Callable(max, range(4)) print callable() Exercise: Draw an object diagram that shows the attributes of callable and their values. Live Free or Die ---------------- A value is considered False if it is a so-called False value: 0, 0.0, '', (), [], {}, None, False All other values are considered True values, including True. Here is a clever way you could implement logical "and": def and(b1, b2): if b1: return b2 return b1 If the first value is True, then we return the second value. Otherwise return the first (which we know is a false value). Similarly, we could implement logical "or" like this: def or(b1, b2): if b1: return b1 return b2 Notice: 1) in "and", if the first value is False, we don't have to evaluate the second 2) in "or", if the first value is True, we don't have to evaluate the second That's why the built-in operators implement SHORT-CIRCUIT EVALUATION res = x and y If x is a False value, res = x, and y is never evaluated If x is a True value, res = y. res = x or y If x is a True value, res = x and y is never evaluated If x is a False value, res = y. This implementation is efficient and lends itself to a design pattern (of sorts) called "Live Free or Die" res = try_to_do_something(args) or die(a_horrible_death) If try_to_do_something succeeds, it returns a True value and everyone is happy. If it fails, it returns a False value and then we have to execute the second half of the or. In CardExample.py: def __cmp__(self, other): return self.suit - other.suit or cmp_rank(self.rank, other.rank) Subtraction is a common way to implement __cmp__ If self.suit and self.rank are different, the difference is a True value, and we return it without executing cmp_rank. If they are the same, the difference is 0, which is a False value, and we have to check the ranks. Oh, the humanity! Needless to say, this pattern is not immediately obvious to the most casual observer, and should be saved for special occasions.