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)这些类就是你要创建的数据模型或类型系统的基础构建模块。 下面就是我们实际定义的各种不同的数据类型:
然后使用这些自定义数据类型,我们定义一个类:
测试这个类的属性赋值约束,可发现对某些属性的赋值违法了约束是不合法的:
还有一些技术可以简化上面的代码,其中一种是使用类装饰器:
另外一种方式是使用元类:
讨论
所有描述器类都是基于混入类来实现的。比如 Unsigned 和 MaxSized 要跟其他继承自 Typed 类混入。 这里利用多继承来实现相应的功能。
混入类的一个比较难理解的地方是,调用 super() 函数时,你并不知道究竟要调用哪个具体类。 你需要跟其他类结合后才能正确的使用,也就是必须合作才能产生效果。
使用类装饰器和元类通常可以简化代码。
所有方法中,类装饰器方案应该是最灵活和最高明的。 首先,它并不依赖任何其他新的技术,比如元类。其次,装饰器可以很容易的添加或删除。
最后,装饰器还能作为混入类的替代技术来实现同样的效果;
这种方式定义的类跟之前的效果一样,而且执行速度会更快。 设置一个简单的类型属性的值,装饰器方式要比之前的混入类的方式几乎快100%。
Last updated
Was this helpful?