Hub Python Library documentation
Строгие Dataclasses
Строгие Dataclasses
Пакет huggingface_hub предоставляет утилиту для создания строгих dataclass. Это расширенные версии стандартного dataclass в Python с дополнительными возможностями валидации. Строгие dataclass обеспечивают проверку полей как при инициализации, так и при присваивании, что делает их идеальными для сценариев, где целостность данных имеет критическое значение.
Обзор
Строгие dataclasses создаются с помощью декоратора @strict. Они расширяют функциональность обычных dataclasses, добавляя:
- Проверка типов полей на основе аннотаций типов
- Поддержка пользовательских валидаторов для дополнительных проверок
- Необязательное разрешение произвольных именованных аргументов в конструкторе
- Проверка полей как при инициализации, так и при присваивании
Преимущества
- Целостность данных: гарантирует, что поля всегда содержат корректные данные
- Простота использования: бесшовная интеграция с модулем
dataclassPython - Гибкость: Поддержка пользовательских валидаторов для сложной логики проверки
- Лёгкость: не требует дополнительных зависимостей, таких как 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
< source >( accept_kwargs: bool = 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 -1validate_typed_dict
Метод для проверки того, что словарь соответствует типам, определённым в классе TypedDict.
Это аналог валидации dataclass, но для TypedDict. Поскольку типизированные словари никогда не инстанцируются (они используются только статическими проверщиками типов), шаг валидации необходимо вызывать вручную.
huggingface_hub.dataclasses.validate_typed_dict
< source >( schema: typedata: dict )
Проверьте, что словарь соответствует типам, определённым в классе 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 -1as_validated_field
Декоратор для создания validated_field. Рекомендуется для полей с одним валидатором, чтобы избежать шаблонного кода.
huggingface_hub.dataclasses.as_validated_field
< source >( validator: Callable )
Декорирует функцию валидатора как validated_field (то есть поле dataclass с кастомным валидатором).
validated_field
Создаёт поле dataclass с пользовательской валидацией.
huggingface_hub.dataclasses.validated_field
< source >( 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 )
Создайте поле dataclass с кастомным валидатором.
Полезно применять несколько проверок к полю. Если применяется только одно правило, обратите внимание на декоратор as_validated_field.
Ошибки
Базовое исключение для строгих dataclasses.
Исключение, возникающее при неправильном определении строгого dataclass.
класс huggingface_hub.errors.StrictDataclassFieldValidationError
< source >( 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не предназначен для высоких нагрузок, где критична производительность. Типичные сценарии использования включают валидацию конфигурации модели (выполняется один раз и пренебрежимо мала по сравнению с запуском модели). Это позволяет нам держать код минимальным.