Hub Python Library documentation

Строгие Dataclasses

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Строгие Dataclasses

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

Обзор

Строгие dataclasses создаются с помощью декоратора @strict. Они расширяют функциональность обычных dataclasses, добавляя:

  • Проверка типов полей на основе аннотаций типов
  • Поддержка пользовательских валидаторов для дополнительных проверок
  • Необязательное разрешение произвольных именованных аргументов в конструкторе
  • Проверка полей как при инициализации, так и при присваивании

Преимущества

  • Целостность данных: гарантирует, что поля всегда содержат корректные данные
  • Простота использования: бесшовная интеграция с модулем dataclass Python
  • Гибкость: Поддержка пользовательских валидаторов для сложной логики проверки
  • Лёгкость: не требует дополнительных зависимостей, таких как Pydantic, attrs или аналогичные библиотеки

Использование

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

from dataclasses import dataclass
from huggingface_hub.dataclasses import strict, as_validated_field

# Custom validator to ensure a value is positive
@as_validated_field
def positive_int(value: int):
    if not value > 0:
        raise ValueError(f"Value must be positive, got {value}")

@strict
@dataclass
class Config:
    model_type: str
    hidden_size: int = positive_int(default=16)
    vocab_size: int = 32  # Default value

    # Methods named `validate_xxx` are treated as class-wise validators
    def validate_big_enough_vocab(self):
        if self.vocab_size < self.hidden_size:
            raise ValueError(f"vocab_size ({self.vocab_size}) must be greater than hidden_size ({self.hidden_size})")

Поля проверяются при инициализации:

config = Config(model_type="bert", hidden_size=24)   # Valid
config = Config(model_type="bert", hidden_size=-1)   # Raises StrictDataclassFieldValidationError

Согласованность между полями также проверяется при инициализации (проверка на уровне класса):

# `vocab_size` too small compared to `hidden_size`
config = Config(model_type="bert", hidden_size=32, vocab_size=16)   # Raises StrictDataclassClassValidationError

Поля также проверяются при присваивании:

config.hidden_size = 512  # Valid
config.hidden_size = -1   # Raises StrictDataclassFieldValidationError

Чтобы повторно выполнить валидацию всего класса после назначения, необходимо явно вызвать .validate:

config.validate()  # Runs all class validators

Пользовательские валидаторы

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

from dataclasses import dataclass
from huggingface_hub.dataclasses import strict, validated_field

def multiple_of_64(value: int):
    if value % 64 != 0:
        raise ValueError(f"Value must be a multiple of 64, got {value}")

@strict
@dataclass
class Config:
    hidden_size: int = validated_field(validator=[positive_int, multiple_of_64])

В этом примере оба валидатора применяются к полю hidden_size.

Дополнительные аргументы по ключевым словам

По умолчанию строгие dataclasses принимают только поля, определённые в классе. Вы можете разрешить дополнительные именованные аргументы, установив accept_kwargs=True в декораторе @strict.

from dataclasses import dataclass
from huggingface_hub.dataclasses import strict

@strict(accept_kwargs=True)
@dataclass
class ConfigWithKwargs:
    model_type: str
    vocab_size: int = 16

config = ConfigWithKwargs(model_type="bert", vocab_size=30000, extra_field="extra_value")
print(config)  # ConfigWithKwargs(model_type='bert', vocab_size=30000, *extra_field='extra_value')

Дополнительные именованные аргументы отображаются в строковом представлении dataclass, но имеют префикс *, чтобы подчеркнуть, что они не проверяются.

Интеграция с Type Hints

Строгие dataclasses соблюдают аннотации типов и автоматически их проверяют. Например:

from typing import List
from dataclasses import dataclass
from huggingface_hub.dataclasses import strict

@strict
@dataclass
class Config:
    layers: List[int]

config = Config(layers=[64, 128])  # Valid
config = Config(layers="not_a_list")  # Raises StrictDataclassFieldValidationError

Поддерживаемые типы включают:

  • Любой
  • Union
  • Необязательно
  • Литерал
  • Список
  • Dict
  • Кортеж
  • Набор

И любые комбинации этих типов. Если вам требуется более сложная валидация типов, вы можете выполнить её с помощью пользовательского валидатора.

Валидаторы классов

Методы с именем validate_xxx считаются классами-валидаторами. Такие методы должны принимать в качестве аргумента только self. Классы-валидаторы выполняются один раз во время инициализации, сразу после __post_init__. Можно определить столько валидаторов, сколько потребуется: они будут выполняться последовательно в том порядке, в котором объявлены.

Обратите внимание, что валидаторы класса не запускаются автоматически повторно при обновлении поля после инициализации. Чтобы вручную выполнить повторную валидацию объекта, необходимо вызвать obj.validate().

from dataclasses import dataclass
from huggingface_hub.dataclasses import strict

@strict
@dataclass
class Config:
    foo: str
    foo_length: int
    upper_case: bool = False

    def validate_foo_length(self):
        if len(self.foo) != self.foo_length:
            raise ValueError(f"foo must be {self.foo_length} characters long, got {len(self.foo)}")

    def validate_foo_casing(self):
        if self.upper_case and self.foo.upper() != self.foo:
            raise ValueError(f"foo must be uppercase, got {self.foo}")

config = Config(foo="bar", foo_length=3) # ok

config.upper_case = True
config.validate() # Raises StrictDataclassClassValidationError

Config(foo="abcd", foo_length=3) # Raises StrictDataclassFieldValidationError
Config(foo="Bar", foo_length=3, upper_case=True) # Raises StrictDataclassFieldValidationError

Метод .validate() является зарезервированным именем для строгих dataclasses. Чтобы предотвратить непредвиденное поведение, будет выброшена ошибка StrictDataclassDefinitionError, если ваш класс уже определяет такой метод.

Справочник API

@strict

Декоратор @strict добавляет к dataclass строгую валидацию.

huggingface_hub.dataclasses.strict

< >

( accept_kwargs: bool = False )

Параметры

  • cls — Класс, который нужно преобразовать в строгий dataclass.
  • accept_kwargs (bool, необязательный) — Если True, разрешает произвольные именованные аргументы в __init__. По умолчанию False.

Декоратор для добавления строгой валидации к dataclass.

Этот декоратор должен использоваться поверх @dataclass, чтобы IDE и инструменты статической типизации распознавали класс как dataclass.

Может использоваться с аргументами или без них:

  • @strict
  • @strict(accept_kwargs=True)

Пример:

>>> from dataclasses import dataclass
>>> from huggingface_hub.dataclasses import as_validated_field, strict, validated_field

>>> @as_validated_field
>>> def positive_int(value: int):
...     if not value >= 0:
...         raise ValueError(f"Value must be positive, got {value}")

>>> @strict(accept_kwargs=True)
... @dataclass
... class User:
...     name: str
...     age: int = positive_int(default=10)

# Initialize
>>> User(name="John")
User(name='John', age=10)

# Extra kwargs are accepted
>>> User(name="John", age=30, lastname="Doe")
User(name='John', age=30, *lastname='Doe')

# Invalid type => raises
>>> User(name="John", age="30")
huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
    TypeError: Field 'age' expected int, got str (value: '30')

# Invalid value => raises
>>> User(name="John", age=-1)
huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
    ValueError: Value must be positive, got -1

validate_typed_dict

Метод для проверки того, что словарь соответствует типам, определённым в классе TypedDict.

Это аналог валидации dataclass, но для TypedDict. Поскольку типизированные словари никогда не инстанцируются (они используются только статическими проверщиками типов), шаг валидации необходимо вызывать вручную.

huggingface_hub.dataclasses.validate_typed_dict

< >

( schema: typedata: dict )

Параметры

  • schema (type[TypedDictType]) — Класс TypedDict, определяющий ожидаемую структуру и типы.
  • data (dict) — Словарь для проверки.

Исключения

StrictDataclassFieldValidationError

  • StrictDataclassFieldValidationError — Если хотя бы одно поле в словаре не соответствует ожидаемому типу.

Проверьте, что словарь соответствует типам, определённым в классе TypedDict.

Под капотом типизированный словарь преобразуется в строгий dataclass и валидируется с помощью декоратора @strict.

Пример:

>>> from typing import Annotated, TypedDict
>>> from huggingface_hub.dataclasses import validate_typed_dict

>>> def positive_int(value: int):
...     if not value >= 0:
...         raise ValueError(f"Value must be positive, got {value}")

>>> class User(TypedDict):
...     name: str
...     age: Annotated[int, positive_int]

>>> # Valid data
>>> validate_typed_dict(User, {"name": "John", "age": 30})

>>> # Invalid type for age
>>> validate_typed_dict(User, {"name": "John", "age": "30"})
huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
    TypeError: Field 'age' expected int, got str (value: '30')

>>> # Invalid value for age
>>> validate_typed_dict(User, {"name": "John", "age": -1})
huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
    ValueError: Value must be positive, got -1

as_validated_field

Декоратор для создания validated_field. Рекомендуется для полей с одним валидатором, чтобы избежать шаблонного кода.

huggingface_hub.dataclasses.as_validated_field

< >

( validator: Callable )

Параметры

  • validator (Callable) — Метод, который принимает значение на вход и вызывает ValueError/TypeError, если значение недействительно.

Декорирует функцию валидатора как validated_field (то есть поле dataclass с кастомным валидатором).

validated_field

Создаёт поле dataclass с пользовательской валидацией.

huggingface_hub.dataclasses.validated_field

< >

( validator: list[collections.abc.Callable[[typing.Any], None]] | collections.abc.Callable[[typing.Any], None]default: typing.Any = <dataclasses._MISSING_TYPE object at 0x7f9675fda620>default_factory: typing.Any = <dataclasses._MISSING_TYPE object at 0x7f9675fda620>init: bool = Truerepr: bool = Truehash: bool | None = Nonecompare: bool = Truemetadata: dict | None = None**kwargs: typing.Any )

Параметры

  • validator (Callable или list[Callable]) — Метод, который принимает значение на вход и вызывает ValueError/TypeError, если значение недействительно. Может быть списком валидаторов для применения нескольких проверок.
  • **kwargs — Дополнительные аргументы для передачи в dataclasses.field().

Создайте поле dataclass с кастомным валидатором.

Полезно применять несколько проверок к полю. Если применяется только одно правило, обратите внимание на декоратор as_validated_field.

Ошибки

класс huggingface_hub.errors.StrictDataclassError

< >

( )

Базовое исключение для строгих dataclasses.

класс huggingface_hub.errors.StrictDataclassDefinitionError

< >

( )

Исключение, возникающее при неправильном определении строгого dataclass.

класс huggingface_hub.errors.StrictDataclassFieldValidationError

< >

( field: strcause: Exception )

Исключение, возникающее при неудачной валидации поля в строгом dataclass.

Зачем не использовать pydantic? (или attrs? или marshmallow_dataclass?)

  • См. обсуждение в https://github.com/huggingface/transformers/issues/36329 о добавлении Pydantic в зависимости. Это было бы тяжелым дополнением и потребовало бы тщательной логики для поддержки обеих версий, v1 и v2.
  • Нам не нужны большинство возможностей Pydantic, особенно те, что связаны с автоматическим приведением типов, jsonschema, сериализацией, псевдонимами и т. д.
  • Нам не нужна возможность создавать экземпляр класса из словаря.
  • Мы не хотим изменять данные. В @strict «validation» означает «проверку того, является ли значение валидным». В Pydantic «validation» означает «приведение значения, возможно, с его изменением, а затем проверку его валидности».
  • Нам не нужна сверхбыстрая валидация. @strict не предназначен для высоких нагрузок, где критична производительность. Типичные сценарии использования включают валидацию конфигурации модели (выполняется один раз и пренебрежимо мала по сравнению с запуском модели). Это позволяет нам держать код минимальным.
Update on GitHub