Best style for Python programs: what do you suggest?

Posted by Noctis Skytower on Stack Overflow See other posts from Stack Overflow or by Noctis Skytower
Published on 2010-06-12T14:22:51Z Indexed on 2010/06/12 19:12 UTC
Read the original article Hit count: 330

A friend of mine wanted help learning to program, so he gave me all the programs that he wrote for his previous classes. The last program that he wrote was an encryption program, and after rewriting all his programs in Python, this is how his encryption program turned out (after adding my own requirements).

#! /usr/bin/env python

################################################################################

"""\
CLASS INFORMATION
-----------------
    Program Name: Program 11
    Programmer:   Stephen Chappell
    Instructor:   Stephen Chappell for CS 999-0, Python
    Due Date:     17 May 2010

DOCUMENTATION
-------------
    This is a simple encryption program that can encode and decode messages."""

################################################################################

import sys

KEY_FILE = 'Key.txt'

BACKUP = '''\
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO\
 PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
_@/6-UC'GzaV0%5Mo9g+yNh8b">Bi=<Lx [sQn#^R.D2Xc(\
Jm!4e${lAEWud&t7]H\`}pvPw)FY,Z~?qK|3SOfk*:1;jTrI'''

################################################################################

def main():
    "Run the program: loads key, runs processing loop, and saves key."
    encode_map, decode_map = load_key(KEY_FILE)
    try:
        run_interface_loop(encode_map, decode_map)
    except SystemExit:
        pass
    save_key(KEY_FILE, encode_map)

def run_interface_loop(encode_map, decode_map):
    "Shows the menu and runs the appropriate command."
    print('This program handles encryption via a customizable key.')
    while True:
        print('''\
MENU
====
(1) Encode
(2) Decode
(3) Custom
(4) Finish''')
        switch = get_character('Select: ', tuple('1234'))
        FUNC[switch](encode_map, decode_map)

def get_character(prompt, choices):
    "Gets a valid menu option and returns it."
    while True:
        sys.stdout.write(prompt)
        sys.stdout.flush()
        line = sys.stdin.readline()[:-1]
        if not line:
            sys.exit()
        if line in choices:
            return line
        print(repr(line), 'is not a valid choice.')

################################################################################

def load_key(filename):
    "Gets the key file data and returns encoding/decoding dictionaries."
    plain, cypher = open_file(filename)
    return dict(zip(plain, cypher)), dict(zip(cypher, plain))

def open_file(filename):
    "Load the keys and tries to create it when not available."
    while True:
        try:
            with open(filename) as file:
                plain, cypher = file.read().split('\n')
                return plain, cypher
        except:
            with open(filename, 'w') as file:
                file.write(BACKUP)

def save_key(filename, encode_map):
    "Dumps the map into two buffers and saves them to the key file."
    plain = cypher = str()
    for p, c in encode_map.items():
        plain += p
        cypher += c
    with open(filename, 'w') as file:
        file.write(plain + '\n' + cypher)

################################################################################

def encode(encode_map, decode_map):
    "Encodes message for the user."
    print('Enter your message to encode (EOF when finished).')
    message = get_message()
    for char in message:
        sys.stdout.write(encode_map[char] if char in encode_map else char)

def decode(encode_map, decode_map):
    "Decodes message for the user."
    print('Enter your message to decode (EOF when finished).')
    message = get_message()
    for char in message:
        sys.stdout.write(decode_map[char] if char in decode_map else char)

def custom(encode_map, decode_map):
    "Allows user to edit the encoding/decoding dictionaries."
    plain, cypher = get_new_mapping()
    for p, c in zip(plain, cypher):
        encode_map[p] = c
        decode_map[c] = p

################################################################################

def get_message():
    "Gets and returns text entered by the user (until EOF)."
    buffer = []
    while True:
        line = sys.stdin.readline()
        if line:
            buffer.append(line)
        else:
            return ''.join(buffer)

def get_new_mapping():
    "Prompts for strings to edit encoding/decoding maps."
    while True:
        plain = get_unique_chars('What do you want to encode from?')
        cypher = get_unique_chars('What do you want to encode to?')
        if len(plain) == len(cypher):
            return plain, cypher
        print('Both lines should have the same length.')

def get_unique_chars(prompt):
    "Gets strings that only contain unique characters."
    print(prompt)
    while True:
        line = input()
        if len(line) == len(set(line)):
            return line
        print('There were duplicate characters: please try again.')

################################################################################

# This map is used for dispatching commands in the interface loop.
FUNC = {'1': encode, '2': decode, '3': custom, '4': lambda a, b: sys.exit()}

################################################################################

if __name__ == '__main__':
    main()

For all those Python programmers out there, your help is being requested. How should the formatting (not necessarily the coding by altered to fit Python's style guide? My friend does not need to be learning things that are not correct. If you have suggestions on the code, feel free to post them to this wiki as well.

© Stack Overflow or respective owner

Related posts about python

Related posts about best-practices