part_01_nil - grossho/billow GitHub Wiki

Объект Nil и сопряженные функции

Введение.

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

Однако один вид объектов все же не был представлен в языке Python — NULL значение. Существует конечно None, но это же значение возвращается Python-функциями и в том случае, когда они не должны возвращать никакого результата, что делало бы недоступным для равнозначного отображения в Python коде понятие процедуры. Ведь None возвращается Python-функциями и методами даже в том случае, когда они не должны возвращать никакого результата.

Но не одно это требование привело к необходимости создания выделенного значения. Агрегирующие функции не учитывают NULL значения при выводе результата. Функции MIN, MAX, COUNT, SUM, AVG и прочие, игнорируют значение NULL в столбце, указанному через её единственный аргумент. Исключение составляет лишь «COUNT(*)», и то лишь потому, что это является специальной формой записи, призванной считать именно количество строк, а не ячеек.

Для Nil значения в ряде случаев может оказаться важным свойство переопределения через локальное, глобальное пространство имен, за счет чего становится возможным построение замыканий с переопределением Nil значения на другое. Напомню, что для имени None переопределение запрещено.

Объект Nil.

Из всех абстракций необходимей всего является перенос NULL. Однако это значение также используется для обозначения в Си-программах, что может запутать в случае использования отладчика. Другим нежеланием использовать в виде имени «NULL» значения синглтона исходит из несоответствия формы для Python среды. То есть «NULL» должно было бы писаться как «Null», но это может быть негативно встречено уже со стороны администраторов БД. Поскольку имя значения не должно появляться в необработанном виде, то мы можем использовать другое имя, отличное от «Null» и «None» (кстати, заметили как в таком начертании они сливаются, и почти неразличимы глазу при их частой перемежевки?).

Тут на помощь пришел язык Ruby, где на «Nil» возложены те же функции, что и на «None» в Python. Ruby on Rails построен как ORM, что рождает правильные ассоциации, поэтому было решено остановиться именно на этом имени.

Стоит отметить одно из свойств типа: что независимо от того, как использовать значение Nil — как левый, или как правый операнд в операции над двумя аргументами — все равно в качестве результата получаем значение Nil.

Это интересное свойство позволяет нам избавиться от всевозможных исключений, которые имели бы место быть, окажись на месте Nil значения, значение None. Это свойство можно указать как интероперабельность объекта.

Как недостаток введения подобного стандартного синглтон-типа то, что проверка должна выполняться за счет операций «is» или «is not».

Аналоги агрегирующих функций.

Уже упоминалось, что короткие имена функций SQL могут пересекаться при их портировании в среду Python. Мы не можем переопределить имеющийся встроенный функционал языка, поэтому специально для исключения пересечений по именам используется расширение строкового представления за счет добавления суффикса «_it» или «_at» в именах функций модуля. Суффикс «_it» используется для итерирования последователностей, передаваемых единственным аргументом. Другой, «_at», используется для итерирования над списком аргументов.

В дополнение к функциям по поиску максимального и минимального значений, добавлена функция, рассчитывающая одновременно оба значения за один проход итератора — «rangowe». Такая оптимизация позволяет уменьшить требуемое количество сравнений: одно, на каждое из четырех. В этой функции также, как и в других, встроена функция игнорирования Nil значений.

Функция «coalesce», действует таким же образом, какое ожидается поведение для SQL: возвращает первое не-Nil значение в представленном кортеже. Соответственно «coalesce_it» также возвращает первое не-Nil значение, только уже для итерируемой последовательности, заданной в виде первого аргумента. Если не найдено ни одного не-Nil элемента, или последовательность пуста (когда «coalesce» вызвана без аргументов — возьмите на заметку в качестве универсальной lambda-функции), то возвратным значением будет Nil.

Функция ожидания одиночного результата.

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

Однако на самом деле ошибки подобного рода могут быть упущены из внимания. В некоторых ORM системах существуют решения, но они жестко привязаны к SQL, хотя характерны не только для него. Поэтому данный примитив был вынесен как обособленная функция, со специфическим поведением.

Функции «expectone» и «expectone_it» не имеют аналогов в SQL, и неявно используется в PL/SQL. Для Python функций ближайшим аналогом является метод модуля sqlalchemy — «query.one». Работает следующим образом:

  • если последовательность пустая или состоит лишь из одних Nil элементов – возвращается Nil;
  • если последовательность содержит ровно одно не-Nil значение – возвращается именно оно;
  • если последовательность содержит более одного не-Nil элемента – генерируется исключение «billow.ExpectOneError» с аргументом в виде списка не-Nil значений, приведших к конфликту.

Исключение «billow.ExpectOneError» базируется на ValueError. Недостатком данного метода является привязка значений к экземпляру объекта ExpectOneError, что в определенных обстоятельствах может привести к зацикливанию инкрементирующих ссылок. Но для работы над простыми типами баз данных применение будет вполне безопасным. Тем более ошибка будет отсылать к архитектурным промахам, которые исправляются на стадии реализации, и не склоны к случайному проявлению.

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

Взаимосвязь «coalesce» и «expectone».

Если «coalesce» обращает внимание на первый не-Nil элемент в списке, то «expectone» обращает внимание на то что этот элемент один, и ровно один, выбрасывая исключение в ином случае. Первый часто используется для последовательного выбора из нескольких элементов, а второй рекомендуется использовать для обработки исключительных ситуаций внутри контекста ожидания относительно высоконадежного источника данных (реляционные базы данных, автогенерируемые файлы — конфигурационные и разметочные данные).