《8.13 实现数据模型的类型约束》由会员分享,可在线阅读,更多相关《8.13 实现数据模型的类型约束(7页珍藏版)》请在金锄头文库上搜索。
1、8.13 实现数据模型的类型约束问题你想定义某些在属性赋值上有限制的数据结构。解决案在这个问题中,你需要在对某些实例属性赋值时进检查。所以你要定义属性赋值 函数,这种情况下最好使描述器。下的代码使描述器实现了个系统类型和赋值验证框架:# Base class. Uses a descriptor to set a value class Descriptor:def _init_(self, name=None, *opts):self.name = namefor key, value in opts.items():setattr(self, key, value)def _set_(se
2、lf, 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)# Desc
3、riptor for enforcing values class Unsigned(Descriptor):def _set_(self, instance, value):if value = 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
4、, value):if len(value) = self.size:raise ValueError(size must be s.name ACME s.shares = 75 s.shares = -10 Traceback (most recent call last):File “, line 1, in File “example.py“, line 17, in _set_super()._set_(instance, value)File “example.py“, line 23, in _set_raise ValueError(Expected = 0) ValueErr
5、or: Expected = 0 s.price = a lot Traceback (most recent call last):File “, line 1, in File “example.py“, line 16, in _set_raise TypeError(expected + str(self.expected_type) TypeError: expected s.name = ABRACADABRA Traceback (most recent call last):File “, line 1, in File “example.py“, line 17, in _s
6、et_super()._set_(instance, value)File “example.py“, line 35, in _set_raise ValueError(size must be 还有些技术可以简化上的代码,其中种是使类装饰器:# Class decorator to apply constraints def check_attributes(*kwargs):def decorate(cls):for key, value in kwargs.items():if isinstance(value, Descriptor):value.name = keysetattr(
7、cls, key, value)else:setattr(cls, key, value(key)return clsreturn decorate# Example check_attributes(name=SizedString(size=8),shares=UnsignedInteger,price=UnsignedFloat) class Stock:def _init_(self, name, shares, price):self.name = nameself.shares = sharesself.price = price另外种式是使元类:# A metaclass tha
8、t applies checking class checkedmeta(type):def _new_(cls, clsname, bases, methods):# Attach attribute names to the descriptorsfor key, value in methods.items():if isinstance(value, Descriptor):value.name = keyreturn type._new_(cls, clsname, bases, methods)# Example class Stock2(metaclass=checkedmeta
9、):name = SizedString(size=8)shares = UnsignedInteger()price = UnsignedFloat()def _init_(self, name, shares, price):self.name = nameself.shares = sharesself.price = price讨论本节使了很多级技术,包括描述器、混类、super() 的使、类装饰器和元 类。不可能在这详细展开来讲,但是可以在8.9、8.18、9.19节找到更多例 。但是,我在这还是要提下个需要注意的点。先,在 Descriptor 基类中你会看到有个 _set_()
10、法,却没有相应的 _get_() 法。如果个描述仅仅是从底层实例字典中获取某个属性值的话,那 么没必要去定义 _get_() 法。所有描述器类都是基于混类来实现的。如 Unsigned 和 MaxSized 要跟其他继承 Typed 类混。这利多继承来实现相应的功能。混类的个较难理解的地是,调 super() 函数时,你并不知道究竟要调 哪个具体类。你需要跟其他类结合后才能正确的使,也就是必须合作才能产效 果。使类装饰器和元类通常可以简化代码。上两个例中你会发现你只需要输次 属性名即可了。# Normal class Point:x = Integer(x)y = Integer(y)# Me
11、taclass class Point(metaclass=checkedmeta):x = Integer()y = Integer()所有法中,类装饰器案应该是最灵活和最明的。先,它并不依赖任何其他新 的技术,如元类。其次,装饰器可以很容易的添加或删除。最后,装饰器还能作为混类的替代技术来实现同样的效果;# Decorator for applying type checking def Typed(expected_type, cls=None):if cls is None:return lambda cls: Typed(expected_type, cls)super_set =
12、cls._set_def _set_(self, instance, value):if not isinstance(value, expected_type):raise TypeError(expected + str(expected_type)super_set(self, instance, value)cls._set_ = _set_return cls# Decorator for unsigned values def Unsigned(cls):super_set = cls._set_def _set_(self, instance, value):if value =
13、 0)super_set(self, instance, value)cls._set_ = _set_return cls# Decorator for allowing sized values def MaxSized(cls):super_init = cls._init_def _init_(self, name=None, *opts):if size not in opts:raise TypeError(missing size option)super_init(self, name, *opts)cls._init_ = _init_super_set = cls._set
14、_def _set_(self, instance, value):if len(value) = self.size:raise ValueError(size must be + str(self.size)super_set(self, instance, value)cls._set_ = _set_return cls# Specialized descriptors Typed(int) class Integer(Descriptor):passUnsigned class UnsignedInteger(Integer):passTyped(float) class Float(Descriptor):passUnsignedclass UnsignedFloat(Float):passTyped(str) class String(Descriptor):passMaxSized class SizedString(String):pass这种式定义的类跟之前的效果样,且执速度会更快。设置个简单的类型属 性的值,装饰器式要之前的混类的式乎快100%。现在你应该庆幸读完 了本节全部内容了吧?_W3Cschool()最的技术知识分享与学习平台此篇内容来于站户上传并发布。