Python attr.s 多类型验证

Python attr.s multiple type validation

如果提供 none,则默认生成 UUID 的 class,如果提供 str,则 validates/creates 生成 UUID 对象。

import attr
from attrs import validators
from uuid import UUID, uuid1

def _validate(instance, attribute, value) -> None:
    try:
        if isinstance(value, str):
            instance.uuid = UUID(value)
        return
    except Exception as e:
        raise BadUUID() from e


@attr.s(slots=True)
class Private:
    uuid = attr.ib(type=[str, UUID], validator=[validators.optional([validators.instance_of(UUID), validators.instance_of(str)]), _validate], default=uuid1())

它应该提供和不提供值:

print(Private())
print(Private('d283a713-9f4b-1c15-ab8d-8d95d7ce8999'))

如果有none,它应该使用默认设置生成一个新的 UUID。

如果提供了值,它应该验证它并创建一个 UUID 对象。


我收到一个错误,因为它只会验证一种实例类型,strUUID

如果我设置 instance_of(UUID) 它只会在不提供值的情况下工作。

如果我将它设置为 instance_of(str),它只会在提供 str 的情况下工作。


我做错了什么吗,有没有更好的方法来完成我正在寻找的东西?

看来这就是你想要的:

import attr
from uuid import UUID, uuid1


def _convert(value) -> UUID:
    return value if isinstance(value, UUID) else UUID(value)


@attr.s(slots=True)
class Private:
    uuid = attr.ib(default=uuid1(), converter=_convert)


p1 = Private()
print(p1.uuid)

p2 = Private('c53358b3-798e-11ec-a49b-cf6d4243e811')
print(p2.uuid)

示例结果:

d5e2d087-798e-11ec-9d59-cf6d4243e811
c53358b3-798e-11ec-a49b-cf6d4243e811

如果您更喜欢 字符串被转换,并且如果传递了任何其他内容则引发您自己的异常(尽管我可能只是将其保留到 UUID()本身),这有效:

import attr
from uuid import UUID, uuid1


class BadUUID(Exception):
    ...


def _convert(value) -> UUID:
    if isinstance(value, str):
        return UUID(value)
    elif isinstance(value, UUID):
        return value
    else:
        raise BadUUID(f'{value} is neither a string nor a UUID')


@attr.s(slots=True)
class Private:
    uuid = attr.ib(default=uuid1(), converter=_convert)


p = Private(42)

或者,如果您的目的是从 UUID 捕获异常并添加一些内容:

import attr
from uuid import UUID, uuid1


class BadUUID(Exception):
    ...


def _convert(value) -> UUID:
    try:
        return value if isinstance(value, UUID) else UUID(value)
    except Exception as e:
        raise BadUUID (f'{value} is not a good UUID') from e


@attr.s(slots=True)
class Private:
    uuid = attr.ib(default=uuid1(), converter=_convert)


p = Private(42)