Пример рефакторинга для устранения дублирования кода при плюрализации


Исходный код

def desyatka(number):
    return int(number/10.0)
    
def edenica(number):
    return number-desyatka(number)*10

hours_string=[]
for a in range(0,30):
    zzz =""
    if edenica(a)<5:
        zzz = " часа "
    if edenica(a)==1:
        zzz = " час "
    if desyatka(a)==1:
        zzz = " часов "
    if edenica(a)==0:
        zzz = " часов "
    if zzz=="":
        zzz=" часов "
    hours_string.append(zzz)

minutes_string=[]
for a inrange(0,60):
    zzz=""
    if edenica(a)<5:
        zzz = " минуты "
    if edenica(a)==1:
        zzz = " минута "
    if desyatka(a)==1:
        zzz = " минут "
    if edenica(a)==0:
        zzz = " минут "
    if zzz=="":
        zzz=" минут "
    minutes_string.append(zzz)
    
seconds_string=[]
for a inrange(0,60):
    zzz=""
    if edenica(a)<5:
        zzz = " секунды "
    if edenica(a)==1:
        zzz = " секунда "
    if desyatka(a)==1:
        zzz = " секунд "
    if edenica(a)==0:
        zzz = " секунд "
    if zzz=="":
        zzz=" секунд "
    seconds_string.append(zzz)

Что не так в исходном коде

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

Само собой разумеется, что знание о получении нужной формы слова — это отдельное самостоятельное знание, которое должно быть отделено от знания о том, что делать с полученной формой слова.

Важно, чтобы возвращаемая форма слова не подразумевала какого-то конкретного использования — то есть не содержала никаких лишних знаков включая пробелы. Это позволит применять результат функции любым способом в любом месте проекта.

Также необходимо скорректировать некоторые названия функций и переменных.

На мой взгляд, перезатирание значений переменных как правило (хотя и не всегда) свидетельствует о недостаточно продуманном алгоритме.

Как можно отрефакторить исходный код

def pluralize(number, word1, word2, word5):
    units = number % 10
    decades = number / 10 % 10if decades == 1:
        return word5

    if units == 1:
        return word1
    elif units >= 2and units <= 4
        return word2
    else
        return word5
hours_string = []
for a in range(0, 30):
    hours_string.append(pluralize(a, "час", "часа", "часов"))

minutes_string = []
for a in range(0, 60):
    minutes_string.append(pluralize(a, "минута", "минуты", "минут"))

seconds_string = []
for a in range(0, 60):
    seconds_string.append(pluralize(a, "секунда", "секунды", "секунд"))

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

Теория