# 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 remove_petal(f), remove_petal1(f) and remove_petal2(f). You can put any of these remove_petaling 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 remove_petal 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 remove_petal(f): """Remove 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 remove_petal1(f): """Remove 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 remove_petal2(f): """Remove 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 '\nRemoving the petals with remove_petal()' while f.num_petals > 0: remove_petal(f) f = Flower() print '\na new flower:', str(f) print 'Removing the petals with remove_petal1()' while f.num_petals > 0: remove_petal1(f) f = Flower() print '\na new flower:', str(f) print 'Removing the petals with remove_petal2()' while f.num_petals > 0: remove_petal1(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 remove_petaled until there are no more petals.""" raw_input("\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 remove_petaled while f.num_petals > 0: raw_input('Press "return" to remove_petal a petal') remove_petal(f) print "\n\n" if __name__ == '__main__': run_flower()