继承验证的直观方式 类

Intuative way to inherit validating classes

我一直在使用这种继承方式来验证在对象实例上设置的值,但我想知道是否有更流畅的方法来执行此操作。

我遵循一个规范,其中特定分类 (Foo) 的项目包含特定组成 (Fe) 的元素。

class Typed:
    def __set__(self, obj, value):
        assert isinstance(value, self._type), 'Incorrect type'


class Integer(Typed):
    _type = int


class Float(Typed):
    _type = float


class Positive(Typed):
    def __set__(self, obj, value):
        super().__set__(obj, value)
        assert value >= 0, 'Positive Values Only Accepted'


class PositiveInteger(Integer, Positive):
    pass


class PositiveFloat(Float, Positive):
    pass


class Sized(Typed):
    def __set__(self, obj, value):
        super().__set__(obj, value)
        assert value <=  2**self.size-1, f'{value} is too High'

class Fe(Sized, PositiveInteger):
    name = 'Integer, 8 bit unsigned'
    size = 8
    

class Foo(Fe):
    name = 'Classificaion1'
    def __set__(self, obj, id):
        super().__set__(obj, id)
        obj._id = id
    def __get__(self, obj, objType=None):
        return obj._id
    def __del__(self):
        pass

如果您确实需要这种抽象级别,这可能是您可以做到的最佳方式。我下面的建议也许可以每 class 节省一行。 如果您有能力定义“大小”和“类型”等属性 在最后的 class 上,可以像这样使用更丰富的基础 class 和包含作为“lambda 函数”的检查的声明结构。

注意__init_subclass__的用法来检查是否所有参数 定义了保护表达式所需的:

from typing import Sequence

GUARDS = {
    "typed": ((lambda self, value: "Incorrect type" if not instance(value, self._type) else None), ("_typed",)),
    "positive": ((lambda self, value: "Only positive values" if value < 0 else None), ()),
    "sized": ((lambda self, value: None if value <= 2 ** self.size - 1 else f"{value} must be smaller than 2**{self.size}"), ("size",)),
}

class DescriptorBase:
    guards: Sequence[str]

    def __init_subclass__(cls):
        _sentinel = object()
        for guard_name in cls.guards:
            guard = GUARDS[guard_name]
            required_attrs = guard[1]
            missing = []
            for attr in required_attrs:
                if getattr(cls, attr, _sentinel) is _sentinel:
                    missing.append(attr)
            if missing:
                    raise TypeError("Guarded descriptor {cls.__name__} did not declare required attrs: {missing}")
    
    def __set_name__(self, owner, name):
        self._name = f"_{name}""

    def __set__(self, instance, value):
        errors = []
        for guard_name in self.guards:
            if (error:= GUARDS[guard_name](self, value)) is not None:
                errors.append(error)
        if errors:
            raise ValueError("\n".join(errors))
        setattr (instance, self._name, value)
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.name)
    
    def __del__(self, instance):
        delattr(instance, self._name)


class Foo(DescriptorBase):
    guards = ("typed", "positive", "sized")
    size = 8
    type_ = int
    # No other code required here: __get__, __set__, __del__ handled in superclass
    
class UseAttr:
    # Actual smart-attr usage:
    my_foo = Foo()

实际上,如果你想要class层次结构,行数较少(不需要在每个class中声明一个__set__方法),也可以使用这种方法: 只需更改 __init_superclass__ 以收集所有超级 class 中的“守卫”, 并在正在定义的 class 上合并一个守卫列表,然后 将您的可组合守卫-classes 定义为:

class Positive(BaseDescriptor):
    guards = ("positive",)

class Sized(BaseDescriptor):
    guards = ("sized",)
    size = None

class Foo(Positive, Sized):
    size = 8

class Fe(Foo):
    name = "Fe name"

实际上,要使其正常工作所需的更改很简单:

    def __init_subclass__(cls):
        _sentinel = object()
        all_guards = []
        for supercls in cls.__mro__:
            all_guards.extend(getattr(supercls, "guards", ()))
        # filter unique:
        seem = {}
        new_guards = []
        for guard in all_guards:
            if guard not in seem:
                new_guards.append(guard)
            seem.add(guard)
        cls.guards = new_guards
        for guard_name in cls.guards:

另请注意,您还可以从每个定义的 class 中收集“GUARDS”注册表的内容,而不必事先将所有内容声明为 lambda。我想你可以从这里得到这个想法。