Рубрики
Без рубрики

Добродетель постепенного развития

Первоначально опубликовано в моем блоге. Вот сегодня задача: Можете ли вы написать инструмент командной строки, который … Tagged с Python, обучение.

Первоначально опубликовано на Мой блог Анкет

Вот сегодня задача: Можете ли вы написать инструмент командной строки, который позволяет преобразовать и из различных единиц измерений?

Например, вы можете ввести «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.

Это был действительно хороший сюрприз по нескольким причинам:

  • Во -первых, когда я подумал только о проблеме, прежде чем начать семинар, я был почти уверен, что мне понадобится гораздо более сложная структура данных.
  • Во -вторых, один из студентов только что отказался верить, что код будет работать, даже после того, как он увидел его в действии в переводчике;)
  • Три, мы убили один комментарий:)

Мы нашли красивый алгоритм и хорошую структуру данных, а не пытаясь решить Все одновременно, но медленно создавая все больше и больше общего кода, избавляясь от твердых ценностей один за другим, и, тщательно думая об именовании.

Я надеюсь, что вы найдете этот подход полезным, и я настоятельно рекомендую вам попробовать использовать его в следующий раз, когда вы реализуете новую функцию.

Ваше здоровье!

Я хотел бы услышать, что вы скажете, поэтому, пожалуйста, не стесняйтесь оставить комментарий ниже или проверить моя страница контакта Для большего количества способов связаться со мной.

  1. Я использую Программирование толпы Во время моих классов Python. Это работает очень хорошо. ↩

Оригинал: “https://dev.to/dmerejkowsky/the-virtue-of-incremental-development-1670”