Alphabet list
Alphabet doesn't need to be a list
, you can just use the ascii_lowercase
string:
# just a copy
alphabet = string.ascii_lowercase
# two copies
alphabet = string.ascii_lowercase * 2
# alternatively
from string import ascii_lowercase as alphabet
Using the Modulo Operator
Instead of using two copies of ascii_lowercase
to cover index overflows, you can use the modulo operation as shown by your linked Wikipedia entry:
alphabet = string.ascii_lowercase
shift = 5
# index is in range(0, 25)
alphabet[(6 + shift) % 26]
'l'
# index would be > 25, so it loops back around
alphabet[(21 + shift) % 26]
'a'
Cipher Lookup with a Dictionary
Now, what you could do is use a dictionary to build the lookups ahead of time so that you don't have to use alphabet.index
:
cipher_lookup = {char: alphabet[(i + shift) % 26] for i, char in enumerate(alphabet)}
Now, there's no more need to track indices:
def cipher(message, shift):
cipher_lookup = {char: alphabet[(i + shift) % 26] for i, char in enumerate(alphabet)}
encrypted = []
for letter in message:
# simply look it up in the dictionary
encrypted.append(cipher[letter])
Checking for Punctuation
Use a set
here, rather than a list. Membership testing for a set
/dict
is a time-constant operation, rather than O(N), where N is the length of the list:
punct = set(string.punctuation)
'a' in punct
False
'.' in punct
True
However, you don't actually have to test for punctuation. You could instead use dict.get
to return the value from the dictionary if it's there, and return the letter if it isn't:
# say shift is 5
cipher_lookup.get('a', 'a')
'f'
# punctuation is not in the cipher
# so we just return that character
cipher_lookup.get('.', '.')
'.'
Predefining encMessage
You can just use:
enc_message = ''.join(enc_list)
Variable Naming
Variable and function names are to be snake_case
with all lowercase letters
Function params
Separate your parameters in your function definitions with a space:
def cipher(message, shift):
input
You don't need to cast message
to str
, since input
only outputs strings
if __name__ == "__main__"
Guard
This will allow you to import this function without executing the program if you wanted to reuse it:
# This goes at the very bottom
if __name__ == "__main__":
message = input("Enter the Message you want to encrypt: ")
shift = int(input("Enter the number of letters for cipher shift: "))
print(cipher(message, shift))
You can put your message
and shift
prompts here as well so that they don't execute unless you are running the program.
Refactored
from string import ascii_lowercase as alphabet
def cipher(message, shift):
cipher_lookup = {char: alphabet[(i + shift) % 26] for i, char in enumerate(alphabet)}
encrypted_message = ""
for letter in message:
encrypted_message += cipher_lookup.get(letter, letter)
return encrypted_message
if __name__ == "__main__":
message = input("Enter the Message you want to encrypt: ")
shift = int(input("Enter the number of letters for cipher shift: "))
print(cipher(message, shift))
String concatenation
It's not usually best practice to use string concatenation, but I think this is a good start to demonstrate the concepts of what's going on in the program. A better practice than the for
loop would be to use a generator expression:
encrypted_message = ''.join((cipher_lookup.get(letter, letter) for letter in message))