Типизация ответов
Если вы не знакомы с таким понятием в python, как "type annotation", то рекомендуем ознакомиться с основами в этом уроке из документации FastAPI.
В этой же статье мы покроем те области, которые связаны непосредственно с pynspd
.
TL;DR
Если вы пишете современное приложение с type hints на Python,
пользуетесь инструментами вроде mypy,
хотите быть уверенным в приходящих от НСПД данных -
используете методы nspd.<method_name>_by_model
или NspdFeature.cast()
.
Приведение к типу в методах
Напомним, что разные методы поиска в pynspd
имеют две вариации - базовая и с использованием модели слоя (с суффиксом _by_model
)
from pynspd import Nspd, NspdFeature
nspd = Nspd()
# Базовый вариант
feat_plain = nspd.search_in_layer(
"77:09:0004002:1",
36048
)
# По модели слоя
feats_typed = nspd.search_in_layer_by_model(
"77:09:0004002:1",
NspdFeature.by_title("Земельные участки из ЕГРН"),
)
Анализатор IDE сообщает нам о разных типах:
В рантайме типы так же отличаются:
print(type(feat_plain))
#> <class 'pynspd.schemas.feature.NspdFeature'>
print(type(feats_typed))
#> <class 'pynspd.schemas._autogen_features.Layer36048Feature'>
NspdFeature
- это базовая модель слоя НСПД. Она ничего не знает о его свойствах - только обрабатывает сырые данные в Pydantic-модель.
Layer36048Feature
- модель слоя (в данном случае "Земельные участки"), в котором есть все метаданные.
Ниже приведен краткое сравнение разницы в работе:
NspdFeature |
LayerXXXFeature |
|
---|---|---|
Доступны все свойства объекта | ||
Доступна работа с геометрией .to_shape() |
||
Валидирование данных, приходящих с НСПД | ||
Подсказки IDE по доступным полям | ||
.model_dump_human_readable() дает результат |
Приведение к типу постфактум
Иногда у нас нет возможности получить типизированный объект.
Например, метод nspd.search_in_theme(...)
всегда возвращает NspdFeature
.
При может быть потребность в преимуществах модели слоя - например, для экспорта свойств в человекочитаемый словарь, .model_dump_human_readable()
.
Для этих целей мы можем воспользоваться методом NspdFeature.cast(...)
и:
- передать в качестве аргумента модель слоя (статическое приведение);
- ничего не передавать и определить модель автоматически (динамическое приведение).
raw_feat: NspdFeature = nspd.search_in_theme("77:09:0004002:1")
# Статическое приведение
props_typed_cast = raw_feat.cast(
NspdFeature.by_title("Земельные участки из ЕГРН")
).properties
# Динамическое приведение
props_plain_cast = raw_feat.cast().properties
Динамически приведенный тип, как подсказывает нам название, не может быть определен статическим анализатором,
поэтому считается базовым классом NspdProperties[OptionProperties]
:
Что за NspdProperties[OptionProperties]
?
GeoJSON объекта из НСПД имеет вид:
{
"type": "Feature",
"geometry": {...},
"properties": {
"category": 1,
"category_name": "test",
...другие свойства слоя...
"options": {
"cad_num": "0:0:0:0",
...другие свойства объекта...
}
}
}
"properties"
- это NspdProperties
.
Т.к. свойства объектов в нем могут быть различными,
NspdProperties
- это generic-класс, у которого в квадратных скобках указан тип для "options"
.
OptionProperties
- это базовый класс для "options"
, в нем нет определений для конкретных типов.
Классы для "options"
с конкретным типом имеют вид OptionsXXX
В то время как после статического приведение IDE знает, что это свойства слоя "Земельных участков":
Что за Option36368
?
Категория слоя "Земельные участки" на НСПД имеет id=36368. Отсюда и имя класса свойств этой категории - Option36368
Но если мы сравним типы свойств этих двух объектов, то они окажутся одинаковыми:
assert type(props_plain_cast) == type(props_typed_cast)
print(props_plain_cast)
#> <class 'pynspd.schemas.properties.NspdProperties[Options36368]'>
Таким образом главное отличие динамического и статического приведение - отсутствие подсказки статического анализа.
Tip
Мы можем приводить тип как всего объекта: feat.cast().properties.options
, как указано в примерах выше.
А можем только его свойств: feat.properties.cast().options
- результат будет идентичен