
Есть одна ловушка читаемости кода, которой легко избежать, если вы о ней знаете; тем не менее она встречается постоянно: это отсутствующие единицы измерения. Рассмотрим три фрагмента кода на Python, Java и Haskell:
Код:
time.sleep(300)
Код:
Thread.sleep(300)
Код:
threadDelay 300
Как это можно понять из кода? А никак. Вам просто нужно знать, что аргументом time.sleep являются секунды, а threadDelay — микросекунды. Если вы часто ищете эту информацию, то рано или поздно её запомните, но как сохранить читаемость кода для людей, никогда не встречавшихся с time.sleep?
Вариант 1: вставить единицу измерения в имя
Вместо этого:
Код:
def frobnicate(timeout: int) -> None:
...
frobnicate(300)
сделаем вот так:
Код:
def frobnicate(*, timeout_seconds: int) -> None:
# The * forces the caller to use named arguments
# for all arguments after the *.
...
frobnicate(timeout_seconds=300)
В первом случае мы даже не можем сказать в месте вызова, что 300 — это таймаут, но даже если бы мы это знали, то 300 чего? Миллисекунд? Секунд? Марсианских дней? И напротив, второй пример совершенно не требует объяснений.
Использование именованных аргументов — удобная возможность для языков, которые её поддерживают, но это не всегда возможно. Даже в Python, где time.sleep определяется с одним аргументом по имени secs, мы не можем вызвать sleep(secs=300) из-за особенностей реализации. В таком случае можно присвоить имя значению.
Вместо этого:
Код:
time.sleep(300)
сделаем так:
Код:
sleep_seconds = 300
time.sleep(sleep_seconds)
Теперь в коде нет неоднозначностей, и он читаем даже без обращения к документации.
Вариант 2: использовать строгие типы
Вместо вставки единиц измерения в имя можно использовать более строгие типы, чем integer или float. Например, мы можем использовать тип duration.Вместо этого:
Код:
def frobnicate(timeout: int) -> None:
...
frobnicate(300)
Сделаем вот так:
Код:
def frobnicate(timeout: timedelta) -> None:
...
timeout = timedelta(seconds=300)
frobnicate(timeout)
Чтобы иметь возможность интерпретировать единицу измерения заданного числа с плавающей запятой, необходимо как-то сообщить о ней. Если вам повезёт, то эта информация будет находиться в имени переменной или аргумента, но если не повезёт, то она будет указана лишь в документации, или не указана вовсе. Однако для значения timedelta нет неоднозначности интерпретаций, это часть типа. Кроме того, это устраняет неоднозначность из кода.
Область применимости
Совет использовать строгие типы или вставлять единицы измерения в имена можно применять не только для переменных и аргументов функций, но и для API, Для просмотра ссылки ВойдиНапример, возвращайте не такое:
Код:
{
"error_code": "E429",
"error_message": "Rate limit exceeded",
"retry_after": 100,
}
а такое:
Код:
{
"error_code": "E429",
"error_message": "Rate limit exceeded",
"retry_after_seconds": 100,
}
Не создавайте таких файлов конфигураций:
Код:
request_timeout = 10
лучше выберите один из этих вариантов:
Код:
request_timeout = 10s
Код:
request_timeout_seconds = 10
И не проектируйте бухгалтерское CLI-приложение таким образом:
Код:
show-transactions --minimum-amount 32
выберите один из этих вариантов:
Код:
show-transactions --minimum-amount-eur 32
Код:
show-transactions --minimum-amount "32 EUR"