Extra Stuff for CS106 Victor Norman CS106 break and continue • break and continue are both ways to alter the flow of a loop • Can be used only inside a loop – for loop or while loop. break • Exits the loop immediately • Control jumps to the next statement after the loop. • Only exits the loop it is in. – not an outer loop. • Very useful if search for something and have found it – no need to go on searching. break Example Searching for val in list nums: found = False for num in nums: if num == i: found = True break if found: print “Found”, val else: print val, “is not in the list.” continue • Control jumps back up to top of loop immediately. • Very useful if you have a large body of statement in a loop and know you don’t want to run any more code for that time through the loop – E.g., very useful for filtering out data you are not interested in. continue Example Filtering out items you don’t need to deal with: only want to handle lines from a file that do not start with # and are not blank lines. for line in datafile: if line.startswith(“#”): continue if line.strip() == “”: continue # 300 lines of code here to deal with # good lines of data Formatted Output • Found in section 8.2. • Problem: using just print it is very hard, if not impossible to get your output to show up just how you want it. – how to print floats with 3 digits behind the decimal? – how to print a string in a “field width” of 20. – etc. Example class Team """Represents a baseball team""" def __init__(self, name, wins, losses): self._name = name self._wins = wins self._losses = losses # add getters, etc. here. # Create a list of 4 teams teams = [ Team("Pirates", 120, 42), Team("Tigers", 105, 57), Team("Yankees", 30, 132), Team("Diamondbacks", 81, 81) ] Example (2) • How would we print out the four teams in a nice table, with 20 spaces for the team name, left-justified, 4 spaces for wins column, 4 for losses, etc.? Solution: use formatted output • the % operator, on strings, is the format operator (not mod operator). • Syntax: – spec-string % (tuple of values) • if there is only one value, it does not have to be in a tuple – produces a string as a result – spec-string is a string containing multiple specifications, one per number of values in tuple. – i.e., if the tuple contains 3 items, there has to be 3 specifications in the spec-string. – n-th item in the tuple uses the n-th specification in the spec-string. Example n = 1 cheese = “Mozzarella” weight = 3.1415926 print “%d. Width of %s is %8.3f kilograms.” % (n, cheese, weight) Output: 1. Weight of Mozzarella is 3.142 kilograms. Specifications in spec-string • Any character that is not a specification is reproduced unchanged in the result. • Specifications: – %d: replace with given integer – %s: replace with given string – %f: replace with given float – %%: put % in the output. Exercise • Write a print statement that prints this out: Hello, my name is <name>. where <name> is in a variable name. • Could do this: print “Hello, my name is ” + name + “.” • Or, use %s. • Answer: print “Hello, my name is %s.” % name Exercise 2 Write the code to print out: 0. Cheddar 1. Venezuelan Beaver Cheese 2. Roquefort when given a list of strings cheeses. Use %d for the number at the beginning. Answer: for i in range(len(cheeses)): print “%d. %s” % (i, cheeses[i]) More details • You can specify the field width and # digits behind a decimal, justification, etc. • %<minfieldwidth>.<precision>f – %8.3f: put the float in a field of 8, with 3 digits behind decimal point. Round it. Right justified. – decimal point takes up one “space”. • %<minfieldwidth>s: right-justified string in the given field width • %<minfieldwidth>d: similar to string. Examples print “%8.3f.*” % 4.5 4.500* print “%-8.3f*” % 4.9999 5.000 * (left justified, 3 spaces trail) print “%-20s:” % “Pirates” Pirates : print “%-20s:” % “Diamondbacks” Diamondbacks : Error Handling Big Question: What should a function do when it cannot do its work because of bad input? E.g., def avg(items): return sum(items) / len(items) What bad inputs might we need to handle? Option 1 def avg(items): if len(items) == 0: return "No items to average" else: return sum(items) / len(items) res = avg(myItems) if res == “No items to average”: do_something() else: do_something_else() Still doesn’t handle a list like this: [“hi”, “there”] Another example You provide a nice Card class. Someone tries to use it this way: myCard = Card(87, “P”) Should your code create this card? If so, it won’t be able to print it, and might not work for other stuff… What should it do instead? (Constructors don’t return anything.) Solution: Exceptions! • Used to report unusual or error condition from a function/method back to the caller. • Terminology: – an exception is raised or thrown in a function/method when the error condition is detected. • e.g., ZeroDivisionError. – an exception can be caught or handled by the caller, or allowed to propagate up to the caller’s caller. Syntax to catch an exception <code> try: <code to run, which might raise an exception> except <ExceptionType>: <code to run when exception is caught> <code> (Lots of other parts available to this too, like else, finally, etc.) Another Example Want code to read in a number from the user, and make sure it is a good number… while True: try: x = float(raw_input(“Enter a number: “)) break except ValueError: print “That was not a number!” Raising an exception • You can throw/raise an exception explicitly in your code, e.g., when you get bad input: class Card: def __init__(self, denom, suit): if suit not in (‘C’, ‘D’, ‘H’, ‘S’): raise ValueError(“suit must be one of C, D, H, or S”) self._suit = suit self._denom = denom • What else should we check? Full Code class Card: def __init__(self, denom, suit): if not isinstance(denom, int): raise TypeError(“denom must be an int”) if denom not in range(2, 15): raise ValueError(“denom must have value 2 to 14”) if not isinstance(suit, str): raise TypeError(“suit must be a string.”) if suit not in (‘C’, ‘D’, ‘H’, ‘S’): raise ValueError(“suit must be one of C, D, H, or S”) # code here to store denom, suit, etc. NOTE: now, when you have a Card object, you *know* it has good state. Pre-defined Exceptions/Errors • https://docs.python.org/2/library/exceptions. html#exception-hierarchy • Exercise: fix the avg() call to raise appropriate exceptions. PYTHONPATH, etc. Q: Where does python look when importing modules? A: The python interpreter looks at the environment variable PYTHONPATH, which is a list of directories. It looks in each directory for the name of the module, and when found, loads it. Can see this PATH from within python… sys.path • To see the python path, start up python and do this: >>> import sys >>> print sys.path ['', '//anaconda/lib/python27.zip', '//anaconda/lib/python2.7', '//anaconda/lib/python2.7/plat-darwin', '//anaconda/lib/python2.7/plat-mac', '//anaconda/lib/python2.7/plat-mac/libscriptpackages', '//anaconda/lib/python2.7/lib-tk', '//anaconda/lib/python2.7/lib-old', '//anaconda/lib/python2.7/lib-dynload', '//anaconda/lib/python2.7/site-packages', '//anaconda/lib/python2.7/site-packages/Sphinx1.3.1-py2.7.egg', '//anaconda/lib/python2.7/sitepackages/aeosa', '//anaconda/lib/python2.7/sitepackages/setuptools-17.1.1-py2.7.egg'] pep8 • A “pep” is a Python Enhancement Proposal. • Documents recording how the language evolves – new features, new releases, suggestions, etc. • PEP-8 is the style guide • You can run your code “through” pep8 to get feedback on how well it conforms. Example Mac52078:distrib_yourself vtn2$ pep8 main.py main.py:20:80: E501 line too long (85 > 79 characters) main.py:21:80: E501 line too long (90 > 79 characters) main.py:23:80: E501 line too long (89 > 79 characters) main.py:25:80: E501 line too long (83 > 79 characters) main.py:26:80: E501 line too long (89 > 79 characters) main.py:30:80: E501 line too long (91 > 79 characters) main.py:38:80: E501 line too long (88 > 79 characters) main.py:47:1: E303 too many blank lines (3) main.py:51:1: E302 expected 2 blank lines, found 1 main.py:69:1: W293 blank line contains whitespace main.py:110:1: W293 blank line contains whitespace main.py:124:80: E501 line too long (85 > 79 characters) main.py:155:5: E301 expected 1 blank line, found 0 main.py:157:5: E301 expected 1 blank line, found 0 main.py:159:5: E301 expected 1 blank line, found 0 main.py:161:5: E301 expected 1 blank line, found 0 main.py:186:80: E501 line too long (83 > 79 characters) main.py:187:80: E501 line too long (90 > 79 characters) main.py:201:1: W293 blank line contains whitespace main.py:224:9: E303 too many blank lines (2) main.py:230:1: E302 expected 2 blank lines, found 1 main.py:269:1: W391 blank line at end of file Dictionary intro Big Question: How would you store the information in a phone book in a data structure in python? Phone book maps names to : • phone numbers • addresses Ideas? • A class that stores all info (phone #, name, address) and is stored in a list, sorted by name? • Q: What operations do you need to do? How fast should they be? • Q: Why is the data ordered in a phone book? Dictionaries, in python • type: dict • one-way mappings, from “keys” “values”. – keys must be immutable. – any value can be stored. • each mapping is called an “entry”. • unordered. • Compared to list?: – list: ordered mapping of consecutive integers to values. • mutable data structure dict Operations • Creation: d = dict() or d = {} d = { ‘a’: 3, ‘b’: ‘hello’, 33: 77, (3, 4): [‘sword’, ‘potion’]} (newlines above just for readability) Lookup / Testing • d[k] : gets value for key k. Error if k not a key in d. • len(d): returns # of entries in d. • d.keys(): list of keys in d. • if k in d: test if key k is in dictionary d. • for k in d: • d.items() returns list of tuples of (key, value) pairs. Insertion : add entry k v to dict d. • if entry with key k exists already, replaces value with v. • d[k] = v Example: count bases bases = { ‘A’: 0, ‘G’: 0, ‘T’: 0, ‘C’: 0} genome = “ATCGGGTCAAAAACCCTTGAGAGAG” for base in bases: bases[base] += 1 Example # dictionary has one word per line. f = open(“dictionary.txt”) # map word length to list of words. wordsByLenDict = {} for w in f.readline(): w = w.strip() # remove newline, spaces. lenw = len(w) if lenw in wordsByLenDict: wordsByLenDict[lenw].append(w) wordsByLenDict[lenw].sort() else: # Add new entry mapping len to list of # words, containing only w at this time. wordsByLenDict[lenw] = [w] Use the dictionary • print all two letter words, one word per line if 2 in wordsByLenDict: # 2 is the key for w in wordsByLenDict[2]: print w • print all words, sorted by length. keys = wordsByLenDict.keys() keys.sort() for k in keys: for w in wordsByLenDict[k]: print w Summary: characteristics of dict • Very fast insertion • Very fast look-up. • Keys much be unique and immutable. – One-to-one or one-to-many relationship. • Values can be anything. • Unordered. Consider the following… • Consider this definition: class Card: def __init__(self, denom, suit): legal_nums = range(2, 15) legal_suits = (‘C’, ‘H’, ‘S’, ‘D’) if denom not in legal_nums: raise ValueError(“Bad #”) if suit not in legal_suits: raise ValueError(“Bad suit”) • Every instance of Card has legal_nums and legal_suits defined during instantiation. (Create 1000 full decks and you create 52000 legal_nums and 52000 legal_suits, all exactly the same.) Class Variables • legal_nums and legal_suits are not dependent on the values passed in to the constructor… • We could make them global… Yuck. • Make them class variables. They exist in the class only, not in each object. Solution class Card: legal_nums = range(2, 15) legal_suits = (‘C’, ‘H’, ‘S’, ‘D’) def __init__(self, denom, suit): if denom not in Card.legal_nums: raise ValueError(“Bad #”) if suit not in Card.legal_suits: raise ValueError(“Bad suit”) # could also reference them via self. Gotchas Consider this: class C: c = 3 def __init__(self): print self.c C.c = 33 print self.c self.c = 77 print C.c print self.c c = C() Output: 3 33 33 77