【问题标题】:Python attr.s multiple type validationPython attr.s 多类型验证
【发布时间】:2022-01-20 00:04:29
【问题描述】:

如果没有提供 UUID,则默认生成 UUID 的类,如果提供 str,则验证/创建 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'))

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

如果提供了值,则应对其进行验证并创建一个 UUID 对象。


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

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

如果我将其设置为 instance_of(str),它只会在提供 str 时起作用。


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

【问题讨论】:

  • 为什么_validate 传递给attribute?如果value 不是字符串会发生什么,目前它只是返回而没有变化?
  • @Grismar 我没有使用attribute,就像在文档中那样。 Private 只能以 str 或为空来启动。如果它以str 启动,那么它应该通过_validate。如果Private 初始化为空,它应该求助于default=uuid1(),它返回一个UUID 对象。我不确定instance.uuid = UUID(value) 是否是正确的设置方式。
  • @Grismar 这就是我想出的,对吗? def _validate(inst, attr, value) -> None: print('---- validate') if isinstance(value, str): try: object.__setattr__(inst, 'uuid', UUID(value)) except Exception as e: raise BadUUID() from e @attr.s(slots=True, frozen=True) class Private: uuid = attr.ib( type=(str, UUID), validator=validators.optional([validators.instance_of((str, UUID)), _validate]), default=uuid1() )
  • 不要在 cmets 中发布代码,如果您认为自己更接近答案,请添加到您的问题中,但提供的答案尚不正确。

标签: python python-3.x python-attrs


【解决方案1】:

看来这就是你所追求的:

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)

【讨论】:

  • 我喜欢_convert 的第一个和第三个选项,但我仍将添加validator=validators.optional([validators.instance_of((str, UUID))]),,因为出于某种原因UUID 将允许int 任何长度。
  • 这是你的解决方案,所以这是你的决定——我只建议最好不要让你的代码的用户感到惊讶,所以如果 UUID 接受一些值,除非你有充分的理由不想要接受它们,你最好不要惹他们 - 人们会期望你的代码接受同样的。
猜你喜欢
  • 2018-08-10
  • 2017-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-20
  • 2011-03-23
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多