开发者社区> 问答> 正文

实现数据模型的类型约束

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

展开
收起
哦哦喔 2020-04-17 15:05:44 870 0
1 条回答
写回答
取消 提交回答
  • 在这个问题中,你需要在对某些实例属性赋值时进行检查。 所以你要自定义属性赋值函数,这种情况下最好使用描述器。
    
    下面的代码使用描述器实现了一个系统类型和赋值验证框架:
    
    # 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)
    这些类就是你要创建的数据模型或类型系统的基础构建模块。 下面就是我们实际定义的各种不同的数据类型:
    
    class Integer(Typed):
        expected_type = int
    
    class UnsignedInteger(Integer, Unsigned):
        pass
    
    class Float(Typed):
        expected_type = float
    
    class UnsignedFloat(Float, Unsigned):
        pass
    
    class String(Typed):
        expected_type = str
    
    class SizedString(String, MaxSized):
        pass
    然后使用这些自定义数据类型,我们定义一个类:
    
    class Stock:
        # Specify constraints
        name = SizedString('name', size=8)
        shares = UnsignedInteger('shares')
        price = UnsignedFloat('price')
    
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price
    然后测试这个类的属性赋值约束,可发现对某些属性的赋值违法了约束是不合法的:
    
    >>> s.name
    'ACME'
    >>> s.shares = 75
    >>> s.shares = -10
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "example.py", line 17, in __set__
            super().__set__(instance, value)
        File "example.py", line 23, in __set__
            raise ValueError('Expected >= 0')
    ValueError: Expected >= 0
    >>> s.price = 'a lot'
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "example.py", line 16, in __set__
            raise TypeError('expected ' + str(self.expected_type))
    TypeError: expected <class 'float'>
    >>> s.name = 'ABRACADABRA'
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "example.py", line 17, in __set__
            super().__set__(instance, value)
        File "example.py", line 35, in __set__
            raise ValueError('size must be < ' + str(self.size))
    ValueError: size must be < 8
    >>>
    还有一些技术可以简化上面的代码,其中一种是使用类装饰器:
    
    # Class decorator to apply constraints
    def check_attributes(**kwargs):
        def decorate(cls):
            for key, value in kwargs.items():
                if isinstance(value, Descriptor):
                    value.name = key
                    setattr(cls, key, value)
                else:
                    setattr(cls, key, value(key))
            return cls
    
        return decorate
    
    # Example
    @check_attributes(name=SizedString(size=8),
                      shares=UnsignedInteger,
                      price=UnsignedFloat)
    class Stock:
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price
    另外一种方式是使用元类:
    
    # A metaclass that applies checking
    class checkedmeta(type):
        def __new__(cls, clsname, bases, methods):
            # Attach attribute names to the descriptors
            for key, value in methods.items():
                if isinstance(value, Descriptor):
                    value.name = key
            return type.__new__(cls, clsname, bases, methods)
    
    # Example
    class Stock2(metaclass=checkedmeta):
        name = SizedString(size=8)
        shares = UnsignedInteger()
        price = UnsignedFloat()
    
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price
    
    2020-04-17 15:05:54
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
面向应用的反范式化数据建模 立即下载
Terark.com ——重新定义数据技术 立即下载
存储分层企业数据存储类型选择与优化 立即下载