Я создал простой преобразователь текста в код Морзе на Python, и мне было интересно, есть ли более простой / короткий способ сделать это. Есть ли способ сгенерировать словарь без необходимости жестко кодировать каждую опцию?
def main():
print("This program translates words to morse coden")
words = input("Enter a word or phrase to be encoded: ")
words = words.upper()
codeDict = createDict()
i = 0
try:
while (i < len(words)):
if (words[i] != " "):
print(codeDict[words[i]], end = " ")
else:
print("/", end = " ")
i += 1
except:
print("nInvalid characters found - refer to morse code alphabet")
def createDict():
code = {'A': '.-',
'B': '-...',
'C': '-.-.',
'D': '-..',
'E': '.',
'F': '..-.',
'G': '--.',
'H': '....',
'I': '..',
'J': '.---',
'K': '-.-',
'L': '.-..',
'M': '--',
'N': '-.',
'O': '---',
'P': '.--.',
'Q': '--.-',
'R': '.-.',
'S': '...',
'T': '-',
'U': '..-',
'V': '...-',
'W': '.--',
'X': '-..-',
'Y': '-.--',
'Z': '--..',
'1': '.----',
'2': '..---',
'3': '...--',
'4': '....-',
'5': '.....',
'6': '-....',
'7': '--...',
'8': '---..',
'9': '----.',
'0': '-----'}
return code
main()
3 ответа
ПРИМЕЧАНИЕ. Вы не указываете версию Python, поэтому я предполагаю, что вы используете последнюю версию Python — я лично использую Python 3.9, поэтому я пишу с учетом этого в качестве предположения. Я написал этот ответ для совместимости с Python 3.6 и новее, есть место, где я даю обходной путь для версий Python 3 до 3.6.
Во-первых, я не уверен, что ты сможешь генерировать эквиваленты азбуки Морзе буквы в любом шаблоне — насколько мне известно, для этого не существует алгоритма. Так что вам все равно придется статически кодировать в списке. Однако у вас есть некоторые ненужные моменты, связанные с этим.
Ненужная функция для создания статического словаря
Вы используете функцию для определения и возврата статического словаря. Вы можете легко улучшить что компонент, определив его один раз в своей программе и повторно используя его повсюду — не беспокойтесь обернуть это в функцию.
Просто определите его в начале вашей программы один раз, не регенерируйте его каждый раз. И затем обратитесь к этой переменной соответственно (я назвал ее LETTERS_TO_MC, чтобы было более понятно, для чего она нужна). Но также добавьте регистр перевода для места — это сократит ваши логические сравнения позже.
LETTERS_TO_MC = {
'A': '.-',
'B': '-...',
'C': '-.-.',
'D': '-..',
'E': '.',
'F': '..-.',
'G': '--.',
'H': '....',
'I': '..',
'J': '.---',
'K': '-.-',
'L': '.-..',
'M': '--',
'N': '-.',
'O': '---',
'P': '.--.',
'Q': '--.-',
'R': '.-.',
'S': '...',
'T': '-',
'U': '..-',
'V': '...-',
'W': '.--',
'X': '-..-',
'Y': '-.--',
'Z': '--..',
'1': '.----',
'2': '..---',
'3': '...--',
'4': '....-',
'5': '.....',
'6': '-....',
'7': '--...',
'8': '---..',
'9': '----.',
'0': '-----',
' ': "https://codereview.stackexchange.com/"
}
Переименовать main()
к translate()
Хотя технически это необязательный случай, обычно функции должны указывать на то, что они на самом деле делать — в данном случае они что-то переводят, так что позвольте позвонить main()
по названию того, что он делает — translate()
.
Уменьшать translate()
чтобы делать только то, что он говорит — перевод — и сделать функцию более функциональной
Давайте уменьшим translate
функция специально для перевода и последующего возврата строки кода Морзе. Мы позволим получению информации от конечного пользователя стать частью следующего предложения по улучшениям, которое я делаю.
Во-первых, просто перебирайте строку напрямую, для этого не требуется числовая индексация. Python может справиться с этим сам.
Во-вторых, из-за того, что мы ранее вносили в словарь пробелы, нам больше не нужен неуклюжий if
заявления, ни print()
утверждения с конкретными конечными аргументами.
В-третьих, недопустимый символ в словах, предоставленных функции, вызовет KeyError
внутри, поэтому давайте поймем эту KeyError и вызовем KeyError с настраиваемым сообщением об ошибке, указывающим, что для преобразования был предоставлен недопустимый символ. Мы займемся отображением этого для конечного пользователя позже в следующем разделе предложений.
def translate(words):
code=""
for char in words.upper():
code += f' {LETTERS_TO_MC[char]} '
return code
Вы заметите, что я использую то, что мы называем ф-струна в translate
функция. ** Это доступно только в Python 3.6 и новее. Если вы используете старую версию Python до Python 3, вам придется использовать строку формата старого стиля, т.е. code += ' {} '.format(LETTERS_TO_MC[char])
вместо строки выше.
Это делает функцию меньше и менее сложной!
Обратите внимание, что в этой функции нет обработки ошибок — если символ не может быть преобразован, Python вызовет KeyError
потому что буквы не существует в словаре. Это будет показано в следующем разделе, где мы сделаем этот скрипт исполняемым!
Использовать if __name__ == "__main__":
для прямой обработки исполнения скрипта
Предположим, что в будущем вы, возможно, захотите иметь возможность импортировать translate
в другом месте как часть сценария. Вот почему мы написали это так же, как и в последнем предложении. Однако вы по-прежнему хотите выполнить этот сценарий напрямую и получить ввод. Итак, давайте сделаем это!
Вместо того, чтобы просто звонить translate()
в конце .py
файл, давайте поместим его под if __name__ == "__main__":
блок в конце. Кроме того, поскольку мы уменьшили translate
функция ТОЛЬКО для перевода, здесь мы можем выполнить биты пользовательского ввода.
if __name__ == "__main__":
print("This program translates words into to morse code.n")
words = input("Enter a word or phrase to be encoded: ")
try:
print(translate(words))
except KeyError:
print("The word or phrase you've provided contains invalid characters - we can only convert the letters A-Z, numbers, and spaces to Morse Code in this program.")
Здесь мы обрабатываем прямое выполнение сценария вашего кода Python (как при вызове python3 morsecode-convert.py
или что-то подобное в командной строке). Мы используем обработку try / except, как и раньше (за исключением того, что мы ЗНАЕМ, что недопустимый символ вызывает ошибку KeyError в translate
, поэтому мы перехватываем только эти исключения и выводим пользователю приятное сообщение об ошибке), но мы делаем это только тогда, когда программа выполняется напрямую как сценарий. Это также предотвращает выполнение translate
без фактических полезных данных, передаваемых в функцию, если вы когда-либо импортируете translate
или ваш LETTERS_TO_MC
dict в другую программу / сценарий / проект Python позже.
Вся переписанная программа с учетом приведенных выше предложений
Обратите внимание, что я помещаю несколько определений кода Морзе на строку в dict (по три на строку), чтобы уменьшить его, чтобы было удобнее читать при прокрутке; вы можете легко расширить его, как я делал ранее в своем ответе, но вы все равно можете легко определить, что в слове, даже таким образом.
LETTERS_TO_MC = {
'A': '.-', 'B': '-...', 'C': '-.-.',
'D': '-..', 'E': '.', 'F': '..-.',
'G': '--.', 'H': '....', 'I': '..',
'J': '.---', 'K': '-.-', 'L': '.-..',
'M': '--', 'N': '-.', 'O': '---',
'P': '.--.', 'Q': '--.-', 'R': '.-.',
'S': '...', 'T': '-', 'U': '..-',
'V': '...-', 'W': '.--', 'X': '-..-',
'Y': '-.--', 'Z': '--..', '1': '.----',
'2': '..---', '3': '...--', '4': '....-',
'5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.', '0': '-----',
' ': "https://codereview.stackexchange.com/"
}
def translate(words):
code=""
for char in words.upper():
code += f' {LETTERS_TO_MC[char]} '
return code
if __name__ == "__main__":
print("This program translates words into to morse code.n")
words = input("Enter a word or phrase to be encoded: ")
try:
print(translate(words))
except KeyError:
print("The word or phrase you've provided contains invalid characters - we can only convert the letters A-Z, numbers, and spaces to Morse Code in this program.")
Как уже упоминалось в комментариях, я не думаю, что есть лучший способ создать отображение кода Морзе. Однако мы можем многое сделать, чтобы сделать этот код более лаконичным и питоническим. Это похоже на код Java, который был напрямую переведен на Python и не использует некоторые функции Python.
Код Морзе диктат
См. Комментарии к вашему вопросу: Нам не нужна функция createDict
, который следует называть create_dict
(PEP 8) нам нужна только переменная:
CHAR_TO_MORSE = {'A': '.-',
...
'0': '-----'}
Никогда не перебирать индексы в Python
В Python этот шаблон обычно не рекомендуется:
i = 0
while (i < len(words)):
print(words[i])
Вам почти никогда не нужно перебирать элементы и обращаться к ним по их индексам. Вместо этого вы должны напрямую перебирать элементы итерации:
for word in words:
print(word)
Если вам нужен индекс, вы можете использовать встроенный enumerate
:
for index, word in enumerate(words):
print(f"{index}: {word}")
words
не является описательным именем переменной для строки. Если я перебираю переменную с именем words
, Я ожидаю получить слова, а не отдельные символы. Я бы изменил это на text
или же message
а затем измените цикл на:
for char in text:
...
Это сразу дает понять, что происходит.
Обработка исключений
Не используйте голый, кроме. Это может затруднить прерывание программ и замаскировать другие проблемы, чем те, которые вы собираетесь выявить. Вместо этого вы должны только поймать KeyError
, поскольку это единственная ошибка, которую вы обрабатываете явным образом.
слова[i] знак равно
Вы выполняете эту проверку на каждой итерации цикла, хотя на самом деле это не особый случай. Это в основном только переводит " "
к "/"
, в остальном логика такая же. Таким образом, вы можете просто добавить его в свой char для отображения Морзе:
CHAR_TO_MORSE = {'A': '.-',
...
'0': '-----',
' ': "https://codereview.stackexchange.com/"}
Вызов main ()
В большинстве случаев вы должны заключить вызов в main()
из сценария в if название == “основной«: условие.
if __name__ == "__main__":
main()
Что делать, если название == “основной«: делать?
Полный код
Я также применил .upper()
непосредственно к возвращаемому значению input(...)
и переместил предложение Exception в цикл, чтобы пользователь мог лучше понять ошибку.
CHAR_TO_MORSE = {'A': '.-',
...
'0': '-----',
' ': "https://codereview.stackexchange.com/"}
def main():
print("This program translates words to morse code", end="nn")
text = input("Enter a word or phrase to be encoded: ").upper()
for char in text:
try:
print(CHAR_TO_MORSE[char], end=" ")
except KeyError:
print(f"nInvalid character found: '{char}' - refer to morse code alphabet")
break
join
и составить список / выражения генератора
Если вы не настроены на печать кода Морзе до недопустимого символа, это идеальный вариант использования для join
вместе с пониманием списка:
try:
print(" ".join(CHAR_TO_MORSE[char] for char in text))
except KeyError:
print(f"nInvalid character found - refer to morse code alphabet")
То, что мы здесь используем, на самом деле не понимание списка, а выражение генератора. Если вам интересно, вы можете узнать больше о Выражения генератора против представлений списков.
Рассмотрение
Thomas Ward и riskypenguin дали отличные комментарии в обзоре:
- Удалите ненужные функции
- Лучшие имена функций
- Используйте главную охрану
- Добавьте место в словарь, чтобы избежать особого случая
- Перебирать индексы
- Не используйте голый, кроме
Я не буду их повторять. Вместо этого я должен упомянуть PEP-8: Руководство по стилю для кода Python имеет соглашения о кодировании, которым должен следовать каждый программист Python. Конкретно …
Именование
Имена переменных и функций должны быть в snake_case
. Это означает createDict()
должен быть назван create_dict()
, и аналогично codeDict
должно быть code_dict
.
Белое пространство
Бинарные операторы обычно должны быть окружены одним пробелом, и вы точно следуете этому правилу.
Однако есть исключение: =
символ в параметрах ключевого слова технически не является бинарным оператором. Здесь в руководстве по стилю не рекомендуется использовать пробелы вокруг =
для параметров ключевого слова. Так что вместо:
print("/", end = " ")
рекомендуется использовать:
print("/", end=" ")
Встроенный перевод
Одна из моих любимых и часто упускаемых из виду функций — str.translate()
. Он используется для перевода символов в строке, что вы и пытаетесь здесь сделать. 'S'
переводится на '...'
, и так далее.
>>> MORSE = {'A': '.-', 'B': '-...', 'C': '-.-.', 'O': '---', 'S': '...'}
>>> morse_table = str.maketrans(MORSE)
>>> "SOS".translate(morse_table)
'...---...'
>>>
Проблема здесь в том, что все точки и тире сошлись вместе. Вы хотите, чтобы после каждого кода был пробел. Самое простое, что вы могли бы сделать, — это добавить пробел в коды.
>>> MORSE = {'A': '.- ', 'B': '-... ', 'C': '-.-. ', 'O': '--- ', 'S': '... '}
>>> morse_table = str.maketrans(MORSE)
>>> "SOS".translate(morse_table)
'... --- ... '
>>>
Единственная проблема — встроенная функция перевода оставит без изменений любой символ, который не найден в таблице перевода. Это оставит неожиданные символы в вашем переводе.
>>> "S.O.S!".translate(morse_table)
'... .--- .... !'
Восклицательный знак в конце оставлен без перевода. Хуже того, периоды остаются непереведенными, что делает .O
в .---
(а J
), а также .S
в ....
(ан H
). В translate
функция удалит любые символы, которые переводятся на None
, но поскольку список недопустимых символов огромен, добавление записи для каждого недопустимого символа нецелесообразно.
Мы можем настроить нашу таблицу перевода так, чтобы она просто возвращала None
для каждого неизвестного ключа:
>>> class MorseTable(dict):
def __missing__(self, key):
return None
>>> morse_table = MorseTable(morse_table)
>>> "S.O.S!".translate(morse_table)
'... --- ... '
Простое удаление недопустимых символов из строки может быть нежелательным, и вопрос не в этом. Он выводил сообщение об ошибке, если входная строка содержала неизвестные символы. Для этого мы можем просто проверить, является ли набор символов во входной строке подмножеством MORSE
ключи словаря:
>>> set("SOS") <= MORSE.keys()
True
>>> set("S.O.S!") <= MORSE.keys()
False
Переработанный код
import sys
MORSE_CODE = {
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
'Y': '-.--', 'Z': '--..',
'1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
'6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
' ': "https://codereview.stackexchange.com/"
}
# Add spaces to codes while creating translation table.
MORSE_TRANS_TABLE = str.maketrans({letter: code + " "
for letter, code in MORSE_CODE.items()})
def translate(message: str) -> str:
"""
Translate a message string into Morse Code
Raise a ValueError if the message contains non-Morse code characters.
>>> translate("SOS")
'... --- ...'
"""
message = message.upper()
if set(message) <= MORSE_CODE.keys():
# Translate message and remove trailing space
return message.translate(MORSE_TRANS_TABLE).strip()
else:
raise ValueError("Invalid Morse Code characters in message")
if __name__ == "__main__":
print("This program translates a message into to morse code.n")
message = input("Enter a word or phrase to be encoded: ")
try:
print(translate(message))
except ValueError:
print("Invalid characters found - refer to morse code alphabet", file=sys.stderr)
Большое спасибо! Это было супер тщательно
— am2021
Ха, кажется, я немного затянул с ответом. Единственное улучшение, которое я предлагаю, — это замена конкатенации строк внутри
translate
с участиемreturn " ".join(LETTERS_TO_MC[char] for char in words.upper())
. Это более питонический и более эффективный афайк. Он также избавится от начальных и конечных пробелов, которые, насколько я могу судить, не предназначены.— рискованный пингвин
@riskypenguin Хороший момент, я написал это, когда устал. Я могу вставить это.
— Томас Уорд