Я только начинаю изучать Python, это мой собственный взгляд на Шифр цезаря просто просматривая документы Python. Я хотел бы знать, как я мог бы сделать это лучше и как бы вы это оценили. Это хорошо?
#caeser cipher
import string
message = str(input("Enter the Message you want to encrypt: "))
shift = int(input("Enter the number of letters for cipher shift: "))
def cipher(message,shift):
encList = []
messageLst = []
alphabet = list(string.ascii_lowercase * 2)
punct = list(string.punctuation)
for i in message:
messageLst.append(i)
for i in messageLst :
for j in alphabet:
if i == j:
replaceChar = alphabet[alphabet.index(j)+(shift)]
encList.append(replaceChar)
break
elif i == " ":
encList.append(i)
break
elif i in punct:
encList.append(i)
break
encMessage = ""
encMessage = encMessage.join(encList)
#print(alphabet)
#print(messageLst)
#print(encList)
print("Encrypted Message is : ", encMessage)
cipher(message,shift)
2 ответа
Список алфавитов
Алфавит не обязательно должен быть list
, вы можете просто использовать ascii_lowercase
нить:
# just a copy
alphabet = string.ascii_lowercase
# two copies
alphabet = string.ascii_lowercase * 2
# alternatively
from string import ascii_lowercase as alphabet
Использование оператора по модулю
Вместо использования двух копий ascii_lowercase
чтобы покрыть переполнение индекса, вы можете использовать операцию по модулю, как показано в вашей связанной записи в Википедии:
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'
Поиск шифра со словарем
Теперь вы могли бы использовать словарь для создания поисков заранее, чтобы вам не приходилось использовать alphabet.index
:
cipher_lookup = {char: alphabet[(i + shift) % 26] for i, char in enumerate(alphabet)}
Теперь отслеживать индексы больше не нужно:
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])
Проверка на пунктуацию
Использовать set
здесь, а не список. Тестирование членства для set
/dict
— это операция с постоянной времени, а не O (N), где N — длина списка:
punct = set(string.punctuation)
'a' in punct
False
'.' in punct
True
Однако на самом деле вам не нужно проверять пунктуацию. Вместо этого вы можете использовать dict.get
чтобы вернуть значение из словаря, если оно есть, и вернуть букву, если его нет:
# 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('.', '.')
'.'
Предварительное определение encMessage
Вы можете просто использовать:
enc_message="".join(enc_list)
Именование переменных
Имена переменных и функций должны быть snake_case
со всеми строчными буквами
Параметры функции
Разделите параметры в определениях функций пробелом:
def cipher(message, shift):
input
Вам не нужно бросать message
к str
, поскольку input
выводит только строки
if __name__ == "__main__"
Сторожить
Это позволит вам импортировать эту функцию, не выполняя программу, если вы хотите ее повторно использовать:
# 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))
Вы можете положить свой message
а также shift
подсказки здесь также, чтобы они не выполнялись, если вы не запускаете программу.
Рефакторинг
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))
Конкатенация строк
Обычно не рекомендуется использовать конкатенацию строк, но я думаю, что это хорошее начало для демонстрации концепций того, что происходит в программе. Лучшая практика, чем for
цикл будет использовать выражение генератора:
encrypted_message="".join((cipher_lookup.get(letter, letter) for letter in message))
C.Nivs сделал отличные выводы о:
- не нужно конвертировать
alphabet
в список - с помощью оператора по модулю (
%
) - сохранение переводов в словаре
- с использованием
set
/dict
а такжеin
для тестов на членство - именование, пробелы, ненужные приведения и инициализации
__main__
охранники
Я не буду повторять их здесь, но, пожалуйста, изучите их и следуйте их советам по этим вопросам.
Дополнительные точки проверки кода
Инварианты цикла
Вы написали этот двойной цикл for:
for i in messageLst :
for j in alphabet:
if i == j:
replaceChar = alphabet[alphabet.index(j)+(shift)]
encList.append(replaceChar)
break
elif i == " ":
encList.append(i)
break
elif i in punct:
encList.append(i)
break
Испытания i == " "
а также i in punct
вложены во внутренний цикл, который зацикливается for j in alphabet
. Обратите внимание, что ни эти тесты, ни код, выполняемый на основе прохождения любого из этих тестов, не зависят от j
. Это неэффективно.
Рассмотрим messageLst
который содержит ['z', 'e', 'b', 'r', 'a']
. Первая итерация внешнего цикла
- назначает
'z'
к переменнойi
, а потом - выполняет тело этого цикла, который является внутренним циклом. Этот цикл:
- назначать
j
к первой буквеalphabet
(ан'a'
), и с тех порi == j
ложно, переходит к проверкеi
это пробел, а если нет, то еслиi
— это знак препинания. Поскольку оба значения были ложными, цикл продолжается и, - назначать
j
ко второй буквеalphabet
(а'b'
), и с тех порi == j
ложно, переходит к проверкеi
это пробел, а если нет, то еслиi
— это знак препинания. Поскольку оба значения были ложными, цикл продолжается и, - назначать
j
до третьей буквыalphabet
(а'c'
), и с тех порi == j
ложно, переходит к проверкеi
это пробел, а если нет, то еслиi
— это знак препинания. Поскольку оба значения были ложными, цикл продолжается и,
- назначать
Обратите внимание на эти проверки для i == ' '
а также i in punct
дублируются … 26 раз в случае письма 'z'
!
Если эти тесты убрать из цикла, мы можем получить более эффективную реализацию:
for i in messageLst :
if i == " ":
encList.append(i)
elif i in punct:
encList.append(i)
else:
for j in alphabet:
if i == j:
replaceChar = alphabet[alphabet.index(j)+(shift)]
encList.append(replaceChar)
break
Ненужное зацикливание
Рассмотрим только внутренний цикл:
for j in alphabet:
if i == j:
...
break
Этот поиск alphabet
для ценностей j
это равно i
, и (из-за break
) останавливается при первом появлении. Когда это вхождение обнаружено, j
будет равно i
.
Это сложный способ написания:
if i in alphabet:
j = i
...
На самом деле вам даже не нужно лишнее j
Переменная. Вы можете просто использовать i
в ...
код.
if i in alphabet:
replaceChar = alphabet[alphabet.index(i) + shift]
encList.append(replaceChar)
Улучшенный код
Исправив инвариант цикла и убрав ненужный цикл (но без применения улучшений из другого ответа), мы получим:
for i in messageLst :
if i in alphabet:
replaceChar = alphabet[alphabet.index(i) + shift]
encList.append(replaceChar)
elif i == " ":
encList.append(i)
elif i in punct:
encList.append(i)
… что, безусловно, является улучшением исходного кода.
Батареи в комплекте
C.Nivs также улучшил конкатенацию строк, что привело к созданию выражения генератора для создания зашифрованного сообщения. Мы можем сделать даже лучше …
Python поставляется с str.translate
функция, которая выполняет подстановку по буквам в строке … именно это и делает шифр Цезаря.
from string import ascii_lowercase as alphabet
def cipher(message: str, shift: int) -> str:
"""
Encode a message using a Caesar Cipher with a user-defined shift on
a 26 letter lowercase alphabet.
"""
shift %= len(alphabet)
code = str.maketrans(alphabet, alphabet[shift:] + alphabet[:shift])
return message.translate(code)
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))
Примечание: Я добавил """docstring"""
и напечатайте подсказки cipher
функция. Оба чрезвычайно полезны при разработке Python. Обязательно изучайте их.