popov . dev

Главная

Библиотека

Статьи

Валидация типов ...

Валидация типов на Python с использованием Pydantic

Pydantic - это библиотека для проверки данных и управления настройками для Python. Она использует аннотации типа Python для проверки и сериализации данных, что делает ее мощным инструментом для разработчиков, которые хотят обеспечить корректность и согласованность своих структур данных. В этой статье мы рассмотрим Pydantic, начиная с базовых примеров и переходя к более сложным вариантам использования. К концу статьи у вас будет полное представление о том, как эффективно использовать Pydantic в своих проектах.

Что такое Pydantic?

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

Начало работы с Pydantic

Сначала установим Pydantic с использованием пакетного менеджера pip:

python3 -m pip install pydantic

Простой пример

Давайте начнем с простого примера, чтобы проиллюстрировать, как работает Pydantic. Мы создадим простую модель для пользователя с именем и возрастом.

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int


user = User(name='Иван Петров', age=30)
print(user)

# Вывод: name='Иван Петров' age=30

В этом примере мы определяем модель пользователя с двумя полями: имя (строка) и возраст (целое число). Pydantic гарантирует, что данные, передаваемые в модель пользователя, соответствуют этим типам. Если мы попытаемся создать пользователя с недопустимыми данными, Pydantic выдаст сообщение об ошибке:

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int


# Выдаст ошибку при валидации значений
invalid_user = User(name='Иван Петров', age='тридцать')
print(invalid_user)

Pydantic выдаст сообщение об ошибке, что поле age должно быть целым числом.

Пример валидации значений

Pydantic не просто проверяет типы; он также может накладывать дополнительные ограничения. Например, мы можем потребовать, чтобы возраст пользователя был больше или равен 18 годам.

from pydantic import BaseModel, Field

class User(BaseModel):
    name: str
    age: int = Field(..., ge=18) # [g]reater или [e]qual

# Выдаст ошибку, так как возраст меньше 18
invalid_user = User(name='John Doe', age=17)

В этом примере функция Field используется для добавления ограничения, согласно которому возраст должен быть больше или равен 18 годам. Если возраст меньше 18, Pydantic выдаст ошибку проверки.

Traceback (most recent call last):
...
pydantic_core._pydantic_core.ValidationError: 1 validation error for User
age
  Input should be greater than or equal to 18 [type=greater_than_equal, input_value=17, input_type=int]
...

Примеры вложенных моделей

Pydantic позволяет создавать более сложные структуры данных путем вложения моделей друг в друга. Это особенно полезно при работе с иерархическими или связанными данными. Давайте рассмотрим пример, в котором у нас есть модель записи в блоге, которая включает автора (представленного пользовательской моделью) и список тегов.

from pydantic import BaseModel
from typing import List

class User(BaseModel):
    name: str
    age: int
    email: str

class BlogPost(BaseModel):
    title: str
    content: str
    author: User
    tags: List[str]

post = BlogPost(
    title = 'Первая запись в блоге',
    content = 'Это содержимое статьи.',
    author = User(name='Иван Петров',
    age = 30,
    email = 'i.petrov777@mail.ru'),
    tags = ['python', 'pydantic', 'post']
)

print(post)

# Вывод:
# title='Первая запись в блоге'
# content='Это содержимое статьи.'
# author=User(name='Иван Петров', age=30, email='i.petrov777@mail.ru')
# tags=['python', 'pydantic', 'post']

В этом примере модель BlogPost содержит вложенную модель User для поля author. Поле author предполагает наличие экземпляра модели User, который создается путем ввода необходимых полей (name, age и email). Поле tags определяется как список строк с использованием подсказки типа List[str]. Это позволяет связать несколько тегов с записью в блоге. При создании экземпляра модели BlogPost мы предоставляем необходимые данные для каждого поля, включая вложенную модель User для автора. Pydantic проверит всю структуру данных, гарантируя, что все поля, включая вложенную модель и список, определены правильно и имеют ожидаемые типы.

Примеры необязательных полей

В некоторых случаях не все поля в модели являются обязательными. Pydantic позволяет определять необязательные поля, используя подсказку о необязательном типе в модуле ввода текста. Давайте изменим предыдущую пользовательскую модель, чтобы сделать поле email необязательным:

from pydantic import BaseModel
from typing import Optional


class User(BaseModel):
    name: str
    age: int
    email: Optional[str] = None


user_without_email = User(name='Иван Петров', age=30)
print(user_without_email)

user_with_email = User(name='Иван Петров', age=25, email='i.petrov777@mail.ru')
print(user_with_email)

# Вывод:
# name='Иван Петров' age=30 email=None
# name='Иван Петров' age=25 email='i.petrov777@mail.ru'

В этом обновленном примере поле email определено как Optional[str] со значением по умолчанию None. Это означает, что поле электронной почты может быть опущено при создании экземпляра модели User. Мы создаем два экземпляра модели User: user_without_email и user_with_email. Экземпляр user_without_email создается без указания адреса электронной почты, в то время как экземпляр user_with_email содержит поле электронной почты. Pydantic проверит данные на основе определенной структуры модели. Если поле электронной почты не указано, по умолчанию оно будет равно None. Если будет предоставлено поле email, Pydantic убедится, что оно имеет правильный тип (строка). Использование необязательных полей обеспечивает гибкость структуры данных и учитывает сценарии, в которых определенные поля не всегда могут быть доступны или обязательны для заполнения.

Дополнительные примеры

Пример пользовательских валидаторов

Pydantic предоставляет возможность определять пользовательскую логику проверки с помощью декоратора @validator. Это особенно полезно, когда у вас есть сложные требования к проверке, которые выходят за рамки встроенных правил проверки. Давайте рассмотрим пример, в котором мы хотим убедиться, что поле имени пользовательской модели всегда содержит пробел.

from pydantic import BaseModel, field_validator
from typing import Optional


class User(BaseModel):
    name: str
    age: int
    email: Optional[str] = None

    @field_validator('name')
    def name_must_contain_space(cls, value):
        if ' ' not in value:
            raise ValueError('Имя должно содержать пробел')
        return value


try:
    user = User(name='Иван Петров', age=30)
    print(user) 

    invalid_user = User(name='ИванПетров', age=30)
except ValueError as e:
    print(str(e))

# Вывод:
# name='Иван Петров' age=30 email=None
# 1 validation error for User
# name
#   Value error, Имя должно содержать пробел [type=value_error, input_value='ИванПетров', input_type=str]

В этом примере мы определяем кастомный валидатор, используя декоратор @field_validator. Декоратор применяется к методу name_must_contain_space, который принимает класс (cls) и значение поля (value) в качестве аргументов. Внутри средства проверки мы проверяем, содержит ли значение пробел. Если это не так, мы вызываем ValueError с соответствующим сообщением об ошибке. Если проверка прошла успешно, мы возвращаем исходное значение.

Мы создаем два экземпляра модели User. Первый экземпляр user, имеет допустимое имя с пробелом, поэтому он проходит проверку. Второй экземпляр invalid_user, имеет имя без пробела, которое запускает пользовательский валидатор и вызывает ValueError. Мы перехватываем исключение и выводим сообщение об ошибке. Используя кастомные валидаторы, вы можете применять специальные правила проверки, которые соответствуют требованиям вашего приложения. Это позволяет более точно контролировать процесс проверки данных.

Пример сложных типов данных

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

from pydantic import BaseModel, HttpUrl
from typing import Dict

class Config(BaseModel):
    settings: Dict[str, str]
    homepage: HttpUrl

config = Config(
    settings={'theme': 'dark', 'notifications': 'enabled'},
    homepage='https://example.com'
)

print(config)

# Вывод:
# settings={'theme': 'dark', 'notifications': 'enabled'}
# homepage=Url('https://example.com/')

В этом примере конфигурационная модель содержит два поля: settings и homepage. Поле настроек определено как словарь (Dict[str, str]), где и ключи, и значения являются строками. Поле homepage определяется с использованием типа HttpUrl, который является кастомным типом, предоставляемым Pydantic для проверки URL-адресов. Мы создаем экземпляр модели конфигурации, предоставляя необходимые данные. Полю настроек присваивается словарь с парами ключ-значение, представляющими параметры конфигурации. Полю домашняя страница присваивается допустимая строка URL.

Pydantic автоматически проверяет данные на основе определенных типов. Он гарантирует, что поле настроек представляет собой словарь со строковыми ключами и значениями, а поле домашней страницы проверяется как допустимый URL-адрес с использованием типа HttpUrl. Используя сложные типы данных, Pydantic позволяет вам определять более сложные структуры данных, соответствующие потребностям вашего приложения. Он обеспечивает встроенную поддержку часто используемых типов, а также позволяет создавать кастомные типы для обработки определенных форматов данных.

Альтернативы Pydantic

Несмотря на то, что Pydantic является мощной и многофункциональной библиотекой для проверки данных и управления настройками на Python, существуют альтернативные библиотеки, которые предлагают аналогичную функциональность. Давайте подробнее рассмотрим две популярные альтернативы:

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

from marshmallow import Schema, fields, validate, ValidationError

class UserSchema(Schema):
    name = fields.Str(required=True)
    age = fields.Int(validate=validate.Range(min=18))
    email = fields.Email()


# Создание экземпляра UserSchema
user_schema = UserSchema()

# Объявим валидный объект
data = {
    'name': 'Иван Петров',
    'age': 25,
    'email': 'i.petrov777@mail.ru'
}

# Проверим соответствие данных схеме
result = user_schema.load(data)

# Вывод проверенных данных
print(result)
# Вывод: {'name': 'Иван Петров', 'age': 25, 'email': 'i.petrov777@mail.ru'}

# Объявим невалидный объект
invalid_data = {
    'name': 'Ирина Петрова',
    'age': 17,
    'email': 'invalid-email'
}

# Попытка пройти валидацию данных
try:
    user_schema.load(invalid_data)
except ValidationError as e:
    print(str(e))

# Вывод:
# {'name': 'Иван Петров', 'age': 25, 'email': 'i.petrov777@mail.ru'}
# {'age': ['Must be greater than or equal to 18.'], 'email': ['Not a valid email address.']}

Cerberus: облегченная и расширяемая библиотека проверки данных для Python. Она позволяет вам определять правила проверки с помощью словаря схем, задавая поля, типы и ограничения. Cerberus фокусируется на простоте и удобстве использования. Он предоставляет минималистичный API для определения схем проверки и сопоставления данных с этими схемами. Он поддерживает широкий спектр типов данных и правил проверки "из коробки". Одним из преимуществ Cerberus является его способность обрабатывать вложенные схемы и структуры данных, ориентированные на документы. Он также предоставляет полезные сообщения об ошибках при сбое проверки, что упрощает отладку и обработку ошибок проверки. Пример схемы Cerberus:

from cerberus import Validator

schema = {
    'name': {'type': 'string', 'required': True},
    'age': {'type': 'integer', 'min': 18},
    'email': {'type': 'string', 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'}
}

# Создадим экземпляр Validator
validator = Validator(schema)

# Объявим валидный объект
data = {
    'name': 'Иван Петров',
    'age': 25,
    'email': 'i.petrov777@mail.ru'
}

# Проверим соответствие данных схеме
is_valid = validator.validate(data)

# Вывод результата валидации
print(is_valid)  # Вывод: True
print(validator.document)
# Вывод: {'name': 'Иван Петров', 'age': 25, 'email': 'i.petrov777@mail.ru'}

# Объявим невалидный объект
invalid_data = {
    'name': 'Ирина Петрова',
    'age': 17,
    'email': 'invalid-email'
}

# Попытка пройти валидацию данных
is_invalid = validator.validate(invalid_data)

# Вывод результатов валидации и ошибок
print(is_invalid)  # Вывод: False
print(validator.errors)

# Вывод:
# True
# {'name': 'Иван Петров', 'age': 25, 'email': 'i.petrov777@mail.ru'}
# False
# {'age': ['min value is 18'], 'email': ["value does not match regex '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'"]}

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

Заключение

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

Комментарии

Для того чтобы оставить свое мнение, необходимо зарегистрироваться на сайте