Первоначально опубликовано на Мой блог Анкет
Вот сегодня задача: Можете ли вы написать инструмент командной строки, который позволяет преобразовать и из различных единиц измерений?
Например, вы можете ввести «3 мили в метрах» и получить «4828.03».
В прошлые выходные я представил этот вызов своим студентам Python, попросив их написать код с нуля. 1
Через 1 час произошло что -то чудесное, чего я никогда не ожидал.
Но позвольте мне рассказать вам полную историю.
Я сказал студентам, что они могут начать с написания некоторого «исследовательского кода».
«Просто жестко кодировать все, что вам нужно, и сохранить все в функции main ()
»,-сказал я.
После нескольких обсуждений мы согласились написать только код, который преобразовал километры в мили, и что мы прочитаем значения из командной строки.
Вот что мы придумали:
import sys def main(): kilometers = float(sys.argv) miles = kilometers / 1.609 print(f"{.2f}", miles) if __name__ == " __main__": main()
Затем я указал, что код не был общим. Действительно, «километры», «Майлз» и «1.609» там жестко кодируются.
Студенты понимали, что была функция трех параметров, ожидающая написания. Итак, мы пошли на чертежную доску, и через некоторое время мы решили выполнить функцию под названием конвертировать (значение, unit_in, unit_out)
.
Обратите внимание, что мы сделали не Сделайте любое предположение о тело функции. Мы просто хотели посмотреть, как main ()
Может стать более общим, и нам все еще разрешили принять участия в жестких кодах:
def convert(value, unit_in, unit_out): coefficient = 1 / 1.609 result = value * coefficient return result def main(): value = float(sys.argv[1]) unit_in = sys.argv[2] unit_out = sys.argv[3] result = convert(value, unit_in, unit_out) print(f"{.2f}", result)
Некоторые примечания:
-
main ()
Функция теперь полностью универсальный , и нам, вероятно, не нужно будет изменить это. - Подпись
преобразовать
Функция почти диктовала синтаксис командной строки:
def convert(value, unit_in, unit_out): ...
# Usage: convert.py value unit_in unit_out $ python3 convert.py 2 meters miles
Теперь пришло время избавиться от жестко кодируемого коэффициента. На этот раз найти имя функции было проще:
def get_coefficient(unit_in, unit_out): ...
Затем мы попытались выяснить, как его реализовать. Мы знали, что нам понадобится словарь, но структура его была неизвестна.
«Вернуться к чертежной доске», – сказал я. «Давайте запишем, как должен выглядеть словарь».
Вот наша первая попытка:
units = { "km": { "miles": 1/1.609, "meters": 1/1000, ....}, "yards": { "miles": 1/1760, "meters": ..., "km": ...} ... }
«Это не сделает», – сказал я. ” Посмотрите, что произойдет, если мы добавим новую единицу измерения, например Ноги
“.
Мы должны:
- Добавьте новый ключ «ноги» к
единицы
Словарь, - Вычислить весь коэффициент для преобразования из
Ноги
всем остальным подразделениям, - и добавить
Ноги
ключ ко всем остальным словарям
Там должен быть лучший способ!
После короткого мозгового штурма мы решили ограничить себя расстояние измерения и Всегда конвертируйте в единицы Si первый.
Итак, мы нарисуем новую структуру единицы
Словарь:
# Coefficients convert from "meters" distances = { "km": 1/1000, "yards": 1.094, "miles": 1/1609, }
А потом мы подумали об алгоритме. Мы нашли три возможности:
- Если мы хотим преобразовать Из метров , нам просто нужно искать коэффициент в словаре
- Если мы хотим преобразовать к счетчикам , мы можем посмотреть коэффициент в словаре и вернуть его обратный
- В противном случае мы объединяем две процедуры выше и возвращаем продукт двух коэффициентов.
«Это выглядит хорошо», – сказал я. ” Давайте попробуем внедрить алгоритм, но только для первого случая и посмотрим, что произойдет ».
Я показал своим ученикам, как они могли бы использовать переводчик Python, чтобы проверить функцию get_coeffiate (), работающую должным образом.
Нам быстро удалось получить первый случай:
def get_coefficient(unit_in, unit_out): # FIX ME: only works with distances for now # Coefficients to convert from "meters" distances = { "km": 1/1000, "yards": 1.094, "miles": 1/1609, } if unit_in == "m": return distances[unit_out] >>> import conversion >>> conversion.get_coefficient("m", "km") 0.001 >>> conversion.get_coefficient("m", "yards") 1.094
«Круто, это работает», – сказал я. ” Давайте посмотрим, что происходит, когда входное значение не в метрах: “
def get_coefficient(unit_in, unit_out): # FIX ME: only works with distances for now # Coefficients to convert from "meters" distances = { "km": 1/1000, "yards": 1.094, "miles": 1/1609, } if unit_in == "m": return distances[unit_out] else: reciprocal_coefficient = 1 / distances[unit_in] return reciprocal_coefficient * distances[unit_out] >>> import conversion >>> conversion.get_coefficient("miles", "yards") 1760
«Посмотрите, насколько читабелен код», – сказал я. “У нас есть значение, которое называется взаимный_коэффим
И мы получаем это, позвонив 1 из -за чего -то другого. Разве это не хорошо? ».
Затем я указал, что ‘else’ после возвращения был ненужным.
def get_coefficient(unit_in, unit_out): # FIX ME: only works with distances for now # Coefficients to convert from "meters" distances = { "km": 1/1000, "yards": 1.094, "miles": 1/1609, } if unit_in == "m": return distances[unit_out] reciprocal_coefficient = 1 / distances[unit_in] return reciprocal_coefficient * distances[unit_out]
А потом это случилось. «Привет», один из студентов сказал: «Что если мы добавим счетчики в словаре расстояний с 1
как значение? Мы могли бы избавиться от первого Если
слишком!”.
«Давай сделаем это», – сказал я:
def get_coefficient(unit_in, unit_out): # FIX ME: only works with distances for now distances = { "m": 1, "km": 1/1000, "yards": 1.094, "miles": 1/1609, } reciprocal_coefficient = 1 / distances[unit_in] return reciprocal_coefficient * distances[unit_out] >>> import conversion >>> conversion.get_coefficient("m", "m") 1 >>> conversion.get_coefficient("km", "m") 1000 >>> conversion.get_coefficient("m", "yards") 1760
И, конечно, это работает. Когда метры
это либо UNIT_IN
или UNIT_OUT
, все операции будут включать умение или деление на 1.
Это был действительно хороший сюрприз по нескольким причинам:
- Во -первых, когда я подумал только о проблеме, прежде чем начать семинар, я был почти уверен, что мне понадобится гораздо более сложная структура данных.
- Во -вторых, один из студентов только что отказался верить, что код будет работать, даже после того, как он увидел его в действии в переводчике;)
- Три, мы убили один комментарий:)
Мы нашли красивый алгоритм и хорошую структуру данных, а не пытаясь решить Все одновременно, но медленно создавая все больше и больше общего кода, избавляясь от твердых ценностей один за другим, и, тщательно думая об именовании.
Я надеюсь, что вы найдете этот подход полезным, и я настоятельно рекомендую вам попробовать использовать его в следующий раз, когда вы реализуете новую функцию.
Ваше здоровье!
Я хотел бы услышать, что вы скажете, поэтому, пожалуйста, не стесняйтесь оставить комментарий ниже или проверить моя страница контакта Для большего количества способов связаться со мной.
Я использую Программирование толпы Во время моих классов Python. Это работает очень хорошо. ↩
Оригинал: “https://dev.to/dmerejkowsky/the-virtue-of-incremental-development-1670”