使用自定义方法验证数据级字段?

发布于 2025-01-27 04:32:59 字数 1486 浏览 4 评论 0原文

在使用Dataclasses时,类型提示很好,但我也寻找的是验证 ,例如最大长度50的字符串,int的int,上限为100, /em>)

无论如何是否有验证传递的值?例如,Pydantic具有这些验证者。我正在寻找本机的东西而不添加外部库。我唯一的解决方案是:

from dataclasses import dataclass

def validate_str(max_length):
    def _validate(f):
        def wrapper(self, value):
            if type(value) is not str:
                raise TypeError(f"Expected str, got: {type(value)}")
            elif len(value) > max_length:
                raise ValueError(
                    f"Expected string of max length {max_length}, got string of length {len(value)} : {value}"  # noqa
                )
            else:
                return f(self, value)

        return wrapper

    return _validate

@dataclass
class Example:
    """Class for keeping track of an item in inventory."""

    @property
    def name(self):
        return self._name

    @name.setter
    @validate_str(max_length=50)
    def name(self, value):
         self._name = value

其中validate_str只是一种自定义装饰器的方法,可以检查提供的值的长度,但随后我重复了自己。 我想以以下方式以某种方式通过同一行dataclass属性传递验证器

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str = validate_somehow()
    unit_price: float = validate_somehow()
    quantity_on_hand: int = 0

While working with dataclasses, type hints are good but what I'm looking also for is a Validation of passed values (such as string of max length 50, int with upper limit of 100 etc)

Is there anyway to validate passed value ? For example, Pydantic has these Validators. I'm looking for something native without adding external libraries. My only solution is:

from dataclasses import dataclass

def validate_str(max_length):
    def _validate(f):
        def wrapper(self, value):
            if type(value) is not str:
                raise TypeError(f"Expected str, got: {type(value)}")
            elif len(value) > max_length:
                raise ValueError(
                    f"Expected string of max length {max_length}, got string of length {len(value)} : {value}"  # noqa
                )
            else:
                return f(self, value)

        return wrapper

    return _validate

@dataclass
class Example:
    """Class for keeping track of an item in inventory."""

    @property
    def name(self):
        return self._name

    @name.setter
    @validate_str(max_length=50)
    def name(self, value):
         self._name = value

where validate_str is just a custom decorator method to check length of provided value, but then I repeat myself.
I would like to pass validator somehow in same row of dataclass attribute as:

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str = validate_somehow()
    unit_price: float = validate_somehow()
    quantity_on_hand: int = 0

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

羅雙樹 2025-02-03 04:32:59

理想的方法是使用 验证器 python Hofth Hof-To Guide 上的示例描述符

例如:

from abc import ABC, abstractmethod
from dataclasses import dataclass, MISSING


class Validator(ABC):

    def __set_name__(self, owner, name):
        self.private_name = '_' + name

    def __get__(self, obj, obj_type=None):
        return getattr(obj, self.private_name)

    def __set__(self, obj, value):
        self.validate(value)
        setattr(obj, self.private_name, value)

    @abstractmethod
    def validate(self, value):
        """Note: subclasses must implement this method"""


class String(Validator):

    # You may or may not want a default value
    def __init__(self, default: str = MISSING, minsize=None, maxsize=None, predicate=None):
        self.default = default
        self.minsize = minsize
        self.maxsize = maxsize
        self.predicate = predicate

    # override __get__() to return a default value if one is not passed in to __init__()
    def __get__(self, obj, obj_type=None):
        return getattr(obj, self.private_name, self.default)

    def validate(self, value):

        if not isinstance(value, str):
            raise TypeError(f'Expected {value!r} to be an str')

        if self.minsize is not None and len(value) < self.minsize:
            raise ValueError(
                f'Expected {value!r} to be no smaller than {self.minsize!r}'
            )

        if self.maxsize is not None and len(value) > self.maxsize:
            raise ValueError(
                f'Expected {value!r} to be no bigger than {self.maxsize!r}'
            )

        if self.predicate is not None and not self.predicate(value):
            raise ValueError(
                f'Expected {self.predicate} to be true for {value!r}'
            )


@dataclass
class A:
    y: str = String(default='DEFAULT', minsize=5, maxsize=10, predicate=str.isupper)  # Descriptor instance
    x: int = 5


a = A()
print(a)

a = A('TESTING!!')
print(a)

try:
    a.y = 'testing!!'
except Exception as e:
    print('Error:', e)

try:
    a = A('HEY')
except Exception as e:
    print('Error:', e)

try:
    a = A('HELLO WORLD!')
except Exception as e:
    print('Error:', e)

try:
    a.y = 7
except Exception as e:
    print('Error:', e)

输出:

A(y='DEFAULT', x=5)
A(y='TESTING!!', x=5)
Error: Expected <method 'isupper' of 'str' objects> to be true for 'testing!!'
Error: Expected 'HEY' to be no smaller than 5
Error: Expected 'HELLO WORLD!' to be no bigger than 10
Error: Expected 7 to be an str

The ideal approach would be to use a modified version of the Validator example from the Python how-to guide on descriptors.

For example:

from abc import ABC, abstractmethod
from dataclasses import dataclass, MISSING


class Validator(ABC):

    def __set_name__(self, owner, name):
        self.private_name = '_' + name

    def __get__(self, obj, obj_type=None):
        return getattr(obj, self.private_name)

    def __set__(self, obj, value):
        self.validate(value)
        setattr(obj, self.private_name, value)

    @abstractmethod
    def validate(self, value):
        """Note: subclasses must implement this method"""


class String(Validator):

    # You may or may not want a default value
    def __init__(self, default: str = MISSING, minsize=None, maxsize=None, predicate=None):
        self.default = default
        self.minsize = minsize
        self.maxsize = maxsize
        self.predicate = predicate

    # override __get__() to return a default value if one is not passed in to __init__()
    def __get__(self, obj, obj_type=None):
        return getattr(obj, self.private_name, self.default)

    def validate(self, value):

        if not isinstance(value, str):
            raise TypeError(f'Expected {value!r} to be an str')

        if self.minsize is not None and len(value) < self.minsize:
            raise ValueError(
                f'Expected {value!r} to be no smaller than {self.minsize!r}'
            )

        if self.maxsize is not None and len(value) > self.maxsize:
            raise ValueError(
                f'Expected {value!r} to be no bigger than {self.maxsize!r}'
            )

        if self.predicate is not None and not self.predicate(value):
            raise ValueError(
                f'Expected {self.predicate} to be true for {value!r}'
            )


@dataclass
class A:
    y: str = String(default='DEFAULT', minsize=5, maxsize=10, predicate=str.isupper)  # Descriptor instance
    x: int = 5


a = A()
print(a)

a = A('TESTING!!')
print(a)

try:
    a.y = 'testing!!'
except Exception as e:
    print('Error:', e)

try:
    a = A('HEY')
except Exception as e:
    print('Error:', e)

try:
    a = A('HELLO WORLD!')
except Exception as e:
    print('Error:', e)

try:
    a.y = 7
except Exception as e:
    print('Error:', e)

Output:

A(y='DEFAULT', x=5)
A(y='TESTING!!', x=5)
Error: Expected <method 'isupper' of 'str' objects> to be true for 'testing!!'
Error: Expected 'HEY' to be no smaller than 5
Error: Expected 'HELLO WORLD!' to be no bigger than 10
Error: Expected 7 to be an str
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文