Начальный · Глава 1 / 18
Данные: что именно мы считаем
После главы: Различить значение, его представление и единицу измерения.
В этой главе
Одинаковые цифры, разные обещания
Библиотекарь видит в карточке число 12. Без названия поля оно бесполезно: это двенадцать экземпляров, двенадцать рублей или двенадцатый том? Компьютер тоже не восстанавливает смысл автоматически.
Начинайте модель с вопросов: что измеряем, в каких единицах, какие значения допустимы, кто отвечает за изменение. Для остатка бумажных книг подходят целые неотрицательные числа; для цены в рублях удобно отдельно договориться о хранении целого количества копеек.
Представление — способ записать значение. Число тринадцать можно записать как 13 или 1101 в двоичной системе: смысл числа не меняется. Но строка «0013» может быть артикулом, в котором начальные нули важны. Превратив её в число без разрешения предметной области, вы потеряете часть идентификатора.
Граница и проверка
Восемь двоичных разрядов дают 256 комбинаций. Если ими кодируют беззнаковые целые, диапазон начинается с нуля и заканчивается на 255. Это свойство выбранного формата, а не любого числа внутри любого языка. В Python целые расширяются по мере необходимости в пределах доступной памяти.
В учебном каталоге отделим идентификатор, название и количество. Название допускает кириллицу и пробелы; идентификатор стабилен даже после переименования. Текст при сохранении в файл требует согласованной кодировки. Не считайте число букв размером файла: в UTF-8 разные символы занимают разное количество байтов.
Буква, кодовая точка и округление
Одна видимая буква не всегда равна одному элементу строки. Например, «é» можно представить одной кодовой точкой или сочетанием e и отдельного ударения. Python len считает кодовые точки, а UTF-8 — байты; воспринимаемые человеком знаки описывают графемные кластеры. Поэтому ограничение «80 символов» сначала требует определения единицы.
Дробь 0,1 не имеет конечной двоичной записи: для обычных float выражение 0.1 + 0.2 == 0.3 ложно. Это не случайная поломка сложения. Выбирайте представление под договор: целые копейки для учебной цены, а для измерений — допустимую погрешность. Округление для показа не исправляет автоматически все предыдущие вычисления.
Разобрать внешнюю запись без потери смысла
Представим импорт строки «0042; 7; 350,00». Разделитель полей и десятичный разделитель выполняют разные функции. Сначала разделяем строку по согласованному формату, затем проверяем наличие всех трёх полей. Артикул оставляем строкой. Количество преобразуем в целое только после проверки допустимой записи. Цена требует договорённости о двух десятичных знаках; преобразование через обычный float может добавить ненужную погрешность до перевода в копейки. Для учебного формата можно отдельно разобрать рубли и копейки как целые части.
Значение «350» двусмысленно лишь пока не определена схема. В поле price_kopecks оно означает три рубля пятьдесят копеек; в поле price_rubles — триста пятьдесят рублей. Поэтому имя поля и версия схемы важнее визуального сходства числа. При обмене между программами добавляйте валюту и единицу явно. Если формат изменился, старый файл нельзя читать по новым правилам без миграции или отказа: успешный разбор цифр не доказывает сохранение смысла.
Проверка на границе представления
Полезная серия тестов включает артикул «0», «0000», обычную строку, пустую строку и очень длинное значение. Для количества проверяются отрицательное число, дробь, строка и логическое значение. Договор может запретить пробелы в идентификаторе, но преобразование strip не следует выполнять незаметно, если пробелы потенциально значимы. Сначала формулируется политика нормализации, затем проверяется, что два разных допустимых идентификатора не слились в один.
Для текста различайте сравнение исходных байтов и сравнение нормализованных пользовательских значений. Два визуально одинаковых названия могут иметь разные Unicode-представления; это важно при поиске, но не оправдывает произвольное изменение байтов подписанного файла. В каталоге удобно хранить оригинальное название и отдельно вычислять нормализованный поисковый ключ. Поисковый ключ можно перестроить, а исходные данные остаются доступными для исправления правил. Такое разделение сохраняет возможность объяснить, почему конкретная запись совпала с запросом.
Разобранный пример
Одна поставка, два представления
У магазина тринадцать книг. Код выводит двоичную запись количества и длину русского названия в символах и байтах. Результат: 0b1101, 5, 10. Здесь каждый символ слова «Книга» занимает два байта UTF-8; это пример, а не правило для всех текстов.
pythoncount = 13
label = 'Книга'
print(bin(count), len(label), len(label.encode('utf-8')))
assert int('1101', 2) == countТеперь самостоятельно
Практика
Опишите поля записи для семи экземпляров книги с артикулом «0042» и ценой 350 рублей. Укажите тип, единицу и одно недопустимое значение каждого числового поля.
Результат: Запись из трёх полей и пояснение, почему артикул не число.
Подсказка
- В одном рубле сто копеек.
Решение и проверка
Подходящая запись: sku = «0042», stock = 7, price_kopecks = 35000. Артикул является строкой: ведущие нули сохраняются, сложение артикулов не имеет смысла.
Для stock недопустимо −1. Для price_kopecks недопустима дробная часть копейки в выбранной модели. Нулевая цена требует отдельного бизнес-решения: бесплатное издание может быть законным случаем. Не подменяйте согласование такого правила предположением программиста.
Проверьте себя без текста
Сколько значений помещается в четыре бита?
Шестнадцать; при беззнаковой нумерации это числа от 0 до 15.
Почему название не лучший идентификатор?
Его могут исправить, перевести или повторить у другой книги.
Одинаково ли число видимых знаков и len строки Python?
Не обязательно: составной знак может содержать несколько кодовых точек.
Проверить по первоисточникам
Конец образца
Полный том содержит 18 глав, решения, итоговый проект и словарь. Продажи откроются после предметной редактуры и подключения магазина.