# Caesar ciphers ![centre-aligned Caesar wheel](caesarwheel1.gif) Letter-by-letter enciphering --- layout: true .indexlink[[Index](index.html)] --- # Enciphering and deciphering ## Arithmetic on letters Convert .plaintext[letter] → .plaintext[number] → .ciphertext[number] → .ciphertext[letter] Functions you will need ```python ord() chr() % ``` * What are good test cases? --- # Using the tools Before doing anything, create a new branch in Git * This will keep your changes isolated Experiment in IPython (ephemeral, for us) Once you've got something working, copy the code into a `.py` file (permanent and reusable) ```python from imp import reload import test reload(test) from test import * ``` Re-evaluate the second cell to reload the file into the IPython notebook When you've made progress, make a Git commit * Commit early and often! When you've finished, change back to `master` branch and `merge` the development branch --- # The [string module](http://docs.python.org/3.3/library/string.html) is your friend ```python import string string.ascii_letters string.ascii_lowercase string.ascii_uppercase string.digits string.punctuation ``` --- # DRY and YAGNI Is your code DRY? --- # Doctest * Why document? * Why test? ```python def caesar_encipher_letter(letter, shift): """Encipher a letter, given a shift amount >>> caesar_encipher_letter('a', 1) 'b' """ if letter in string.ascii_letters: . . . ``` --- # The magic doctest incantation ```python if __name__ == "__main__": import doctest doctest.testmod() ``` --- # Doing the whole message ## Test-first developement 1. Write the tests. * They will fail. There is no code. 2. Write code until the tests pass. 3. Refactor. --- # Doing the whole message ## Abysmal ```python ciphertext = '' for i in range(len(plaintext)): ciphertext += caesar_encipher_letter(plaintext[i], key) ``` Try it in IPython --- # Doing the whole message ## Bad ```python ciphertext = '' for p in plaintext: ciphertext += caesar_encipher_letter(p, key) ``` ...but easily generalisable --- # Doing the whole message ## Good (but unPythonic) ```python ciphertext = map(lambda p: caesar_encipher_letter(p, key), plaintext) ``` --- # Doing the whole message ## Best ```python ciphertext = [caesar_encipher_letter(p, key) for p in plaintext] ``` --- # Not all iterables are equal ```python ''.join() ```