14
\$\begingroup\$

I wrote this program to do a simple Caesar shift by a user inputted key, and then deshift. I'm really enjoying it, but I've run out of ideas on improvements! Can you think of anything?

def decrypt():
    a=raw_input("Give me the word to decrypt:")
    number=input("What was it shifted by?")
    b=list(a)
    str(b)
    c=[ord(x)for x in(b)]
    d=[]
    for i in c:
        d.append(i-number)
    e=[chr(i) for i in (d)]
    e="".join(e)
    print "Decryption Successful, your word is",e,"!"

def encrypt():
    a=raw_input("Give me a word:")
    number=input("Give me a number:")
    b=list(a)
    str(b)
    c=[ord(x)for x in(b)]
    d=[]
    for i in c:
        d.append(i+number)
    e=[chr(i) for i in (d)]
    e="".join(e)
    print "Your Caesar shifted result (ascii code) is:",e,"!"
    print "Your key is", number, ",remember that!"

def menu():    
    print "\n\n\nWelcome to the Caesar Shifter."
    print "What would you like to do?"
    print "Option 1:Encrypt Word"
    print "Option 2:Decrypt Word"
    print "If you would like to quit, press 0."
    choice=input("Pick your selection:")
    if choice==1:
        run=encrypt()
        run
        menu()
    elif choice==2:
        derun=decrypt()
        derun
        menu()
    elif choice==0:
        quit
    else:
        print"That is not a correct selection, please pick either 1, or 2."

menu()
\$\endgroup\$
2
  • \$\begingroup\$ Agree with previous comment. One "easy way" to often improve code is to use more (smaller) functions that do less but do whatever they do really well -- it makes the program more modular, testable, and the function names (if chosen correctly) add self-documentation. \$\endgroup\$
    – pst
    Jun 29, 2011 at 18:23
  • \$\begingroup\$ So I should break up my functions more? So when say "encrypt" is run it calls 2 sub functions as opposed to just running that chunk of code? \$\endgroup\$
    – SecNewbie
    Jun 29, 2011 at 18:30

3 Answers 3

8
\$\begingroup\$

This might not be quite what you've got in mind, but one big improvement you could make would be to use meaningful variable names and insert whitespace.

def decrypt():
    cyphertext = raw_input('Give me the word to decrypt:')
    shift = input('What was it shifted by?')

    cypher_chars = list(cyphertext)
    str(b) # this does nothing; you should remove it
    cypher_ords = [ord(x) for x in cypher_list]
    plaintext_ords = []
    for i in cypher_ords:
        plaintext_ords.append(i - shift)

    plaintext_chars = [chr(i) for i in plaintext_ords]
    plaintext = ''.join(plaintext_chars)
    print 'Decryption Successful, your word is', plaintext, '!'

Of course you could actually compress much of this into a one-liner. But for a new programmer, I'd suggest sticking with readable variable names that make it clear what's going on.

Still, you could do that while compressing the code a bit:

def decrypt():
    cyphertext = raw_input('Give me the word to decrypt:')
    shift = input('What was it shifted by?')

    cypher_ords = [ord(x) for x in cyphertext]
    plaintext_ords = [o - shift for o in cypher_ords]
    plaintext_chars = [chr(i) for i in plaintext_ords]
    plaintext = ''.join(plaintext_chars)
    print 'Decryption Successful, your word is', plaintext, '!'
\$\endgroup\$
2
  • \$\begingroup\$ Ok thanks, is it more a style system change so it's more interpretable? \$\endgroup\$
    – SecNewbie
    Jun 29, 2011 at 18:31
  • \$\begingroup\$ @SecNewbie, yes, exactly. Using variable names like a, b, etc is ok if you're planning to throw those variables away immediately or on the next line. But it's much easier for others to read your code if you give descriptive names to variables that are used throughout the function. For that matter, it's much easier for you to read your code, after setting it aside and returning to it after six months! \$\endgroup\$
    – senderle
    Jun 29, 2011 at 18:40
3
\$\begingroup\$

There are several improvements that you can make. As others have said, whitespace helps readability. Take a look at PEP8, the official Python style guide. This guide will help you style your code so that it looks and feels more Pythonic.

One general note, the input function returns a string. So when you do arithmatic like:

>>>should_be_a_num = input('Provide an integer')
>>>print(42 - should_be_a_num)

an error will be raised. Make sure you caste the input to an int.

I also recommend a structure change. Let the menu function handle all user interaction (i.e printing to the screen and recieving input).

decrypt()

There are several improvements here.

b=list(a)

It seems you wanted to put the work into a list so it can be used in a list comprehension. However, this is not needed. The string type in Python is iterable, which means that it can used in a list comprehension:

list_of_characters = [char for char in 'hello world!']

Furthermore, you do not need the parens after in inside list comprehensions. The syntax in my above example works.

str(b)

This line does nothing to help decrypt the text.

Also this can be done in 1 line. Return the decrypted value and just let the menu function handle all printing.

def decrypt(text, shift):
    return ''.join([chr(ord(char) - shift) for char in text])

encrypt()

Many of the same sentiments apply here as they do in your decrypt function, so see that section for the details. Here is my encrypt function:

def encrypt(text, shift):
    return ''.join([chr(ord(char) + shift) for char in text])

menu()

Instead of recursively calling itself, we can use a simple while loop. I also took the liberty of changing around your menu a tad so that the function could be streamlined.

def menu():    
    print "\n\n\nWelcome to the Caesar Shifter."

    # Set the beginning choice and store the function objects.
    choice = -1
    choices = {'1':encrypt, '2':decrypt}

    # Loop as long as the user doesn't choose `quit`.
    while choice != '3':
        print "\n\nWhat would you like to do?"
        print "Option 1: Encrypt Word"
        print "Option 2: Decrypt Word"
        print "Option 3: Exit."

        choice = input("Pick your selection:")

        # Try and get the function. If this errors its because the the choice
        # was not in our options. 
        try:
            func = choices[choice]
        except KeyError:
            if choice != '3':
                print 'Incorrect selection. Please choose either 1, 2, or 3.'
            continue

        text = raw_input("Please give me a word:")
        shift = input("Please provide a shift amount:")
        func(text, int(shift))
\$\endgroup\$
1
  • \$\begingroup\$ +1 for being the only one to put encrypt and decrypt in functions on their own, not related to the input logic. You can do one step further by defining decrypt in terms of encrypt. Also, I reckon the modulo operator can be useful. \$\endgroup\$
    – SylvainD
    May 16, 2014 at 15:15
1
\$\begingroup\$

I have to recommend against use of input. Input allows the user to enter arbitrary python expressions which is not what you want. Instead use int(raw_input()) to get a number from the user.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.