8.13 实现数据模型的类型约束

问题

你想定义某些在属性赋值上面有限制的数据结构。

解决方案

你需要在对某些实例属性赋值时进行检查。 所以你要自定义属性赋值函数,这种情况下最好使用描述器。

# Base class. Uses a descriptor to set a value
class Descriptor:
    def __init__(self, name=None, **opts):
        self.name = name
        for key, value in opts.items():
            setattr(self, key, value)

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value


# Descriptor for enforcing types
class Typed(Descriptor):
    expected_type = type(None)

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError('expected ' + str(self.expected_type))
        super().__set__(instance, value)


# Descriptor for enforcing values
class Unsigned(Descriptor):
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError('Expected >= 0')
        super().__set__(instance, value)


class MaxSized(Descriptor):
    def __init__(self, name=None, **opts):
        if 'size' not in opts:
            raise TypeError('missing size option')
        super().__init__(name, **opts)

    def __set__(self, instance, value):
        if len(value) >= self.size:
            raise ValueError('size must be < ' + str(self.size))
        super().__set__(instance, value)

这些类就是你要创建的数据模型或类型系统的基础构建模块。 下面就是我们实际定义的各种不同的数据类型:

然后使用这些自定义数据类型,我们定义一个类:

测试这个类的属性赋值约束,可发现对某些属性的赋值违法了约束是不合法的:

还有一些技术可以简化上面的代码,其中一种是使用类装饰器:

另外一种方式是使用元类:

讨论

所有描述器类都是基于混入类来实现的。比如 UnsignedMaxSized 要跟其他继承自 Typed 类混入。 这里利用多继承来实现相应的功能。

混入类的一个比较难理解的地方是,调用 super() 函数时,你并不知道究竟要调用哪个具体类。 你需要跟其他类结合后才能正确的使用,也就是必须合作才能产生效果。

使用类装饰器和元类通常可以简化代码。

所有方法中,类装饰器方案应该是最灵活和最高明的。 首先,它并不依赖任何其他新的技术,比如元类。其次,装饰器可以很容易的添加或删除。

最后,装饰器还能作为混入类的替代技术来实现同样的效果;

这种方式定义的类跟之前的效果一样,而且执行速度会更快。 设置一个简单的类型属性的值,装饰器方式要比之前的混入类的方式几乎快100%。

Last updated

Was this helpful?