# flower.py # Lillian Lee (LJL2@cornell.edu), Steve Marschner (srm@cs.cornell.edu) # The idea to have an if-statement based on "he loves me", # which was the inspiration for this demo, is due to J. Kleinberg # Feb 14, 2013 """Module that when run, simulates the 'he loves me' flower ritual. This demo was developed for the purposes of demonstrating if-statements and conditional expressions; see the functions pluck(f), pluck1(f) and pluck2(f). You can put any of these plucking functions into run_flower() and see what happens. It also happens to demonstrate primitive versions of useful stuff that we haven't talked about (yet): defining your own class of objects, prompting a user for input, and doing something repeatedly via while-loops. A number of design choices were made in writing this code, simple as it may seem, and several alternatives are possible.""" import random class Flower(object): """An instance is a flower. Flowers have two attributes. We describe these below by giving the 'class invariants' in comments. Programmers are assumed to write code that maintains the class invariants; they are constraints that are assumed to hold before and after code is executed. num_petals: the number of petals it has. A non-negative int. loves_me_not: True if and only if the next petal to pluck corresponds to "he loves me" (vs. "he loves me not"), assuming there is at least one more petal. If there are no more petals, the value of loves_me_not is None.""" MAX_PETALS = 5 # max number of petals a flower can have # (chosen so that in-lecture demo wouldn't # take too long) # METHODS def __init__(self): """Constructor: A new flower() with num_petals set to a random int in [1,MAX_PETALS], and he_loves_me set to True. Called by flower.Flower() or Flower(), depending on where the function call is made. """ self.num_petals = random.randrange(1, Flower.MAX_PETALS+1) self.he_loves_me = True def __str__(self): """Returns: Readable, somewhat whimsical string representation of this flower. The petals are shown as vertical bars. Sample outputs are: '||| he loves me' '|| he loves me not' 'no more petals' This function uses string * . """ if self.num_petals > 0: return ('|' * self.num_petals + ' he loves me' + ('' if self.he_loves_me else ' not')) else: return 'no more petals' def pluck(f): """Pluck a petal in the 'he loves me' ritual Prints out the appropriate 'he loves me' or 'he loves me not' message, and then removes a petal. An exclamation point is added to the printout if this was the last petal. This implementation uses if-statements with complex booleans. Precondition: f is a Flower object, and has at least one petal. Prints out a warning and exits if f is a Flower object without petals""" if f.num_petals == 0: print 'no more petals' return if f.he_loves_me and f.num_petals > 1: print "he loves me" elif f.he_loves_me and f.num_petals == 1: print "he loves me!" elif not f.he_loves_me and f.num_petals > 1: print "he loves me not" else: print "he loves me not!" # maintain class invariants (i.e., the constraints on attributes # given in the class docstring) f.num_petals = f.num_petals - 1 f.he_loves_me = not f.he_loves_me if f.num_petals > 0 else None def pluck1(f): """Pluck a petal in the 'he loves me' ritual Prints out the appropriate 'he loves me' or 'he loves me not' message, and then removes a petal. An exclamation point is added to the printout if this was the last petal. This implementation uses nested if-statements. Precondition: f is a Flower object, and has at least one petal. Prints out a warning and exits if f is a Flower object without petals""" if f.num_petals == 0: print 'no more petals' return if f.he_loves_me: if f.num_petals > 1: print "he loves me" else: print "he loves me!" else: # he doesn't love me if f.num_petals > 1: print "he loves me not" else: print "he loves me not!" # maintain class invariants on f.he_loves_me and f.num_petals f.num_petals = f.num_petals - 1 f.he_loves_me = not f.he_loves_me if f.num_petals > 1 else None def pluck2(f): """Pluck a petal in the 'he loves me' ritual Prints out the appropriate 'he loves me' or 'he loves me not' message, and then removes a petal. An exclamation point is added to the printout if this was the last petal. This implementation uses if-statements and conditional expressions. Precondition: f is a Flower object, and has at least one petal. Prints out a warning and exits if f is a Flower object without petals""" if f.num_petals == 0: print 'no more petals' return if f.he_loves_me: print "he loves me" + ('' if f.num_petals > 1 else '!') else: print "he loves me not" + ('' if f.num_petals > 1 else '!') # maintain class invariants on f.he_loves_me and f.num_petals f.num_petals = f.num_petals - 1 f.he_loves_me = not f.he_loves_me if f.num_petals > 0 else None def test(): """Some quick-and-dirty sanity-check code to see that things seem reasonable""" print "Running some sanity checks\n" f = Flower() print 'a new flower:', str(f) print '\nPlucking the petals with pluck()' while f.num_petals > 0: pluck(f) f = Flower() print '\na new flower:', str(f) print 'Plucking the petals with pluck1()' while f.num_petals > 0: pluck1(f) f = Flower() print '\na new flower:', str(f) print 'Plucking the petals with pluck2()' while f.num_petals > 0: pluck1(f) f.num_petals = 0 print 'flower with no petals:', str(f) def run_flower(): """Simulate the 'he loves me' ritual with the user. Uses a while loop, where petals are plucked until there are no more petals.""" raw_input("\n\nHAPPY VALENTINE'S DAY!\n\nPress 'return' to pick a flower") f = Flower() # don't need flower.Flower() because inside flower.py print "New flower picked. Let's see...\n" # invariant: if the flower had n petals when created, # then the flower still has petals 1 .. f.num_petals # and petals f.num_petals+1 .. n have been plucked while f.num_petals > 0: raw_input('Press "return" to pluck a petal') pluck(f) print "\n\n" if __name__ == '__main__': run_flower()