在上一篇博客中从类属性、实例属性、到特性、到描述符,创建了特性函数、描述符控制我们类的属性的一些数据验证,今天完成以下工作:
描述符完成两件事:
储存属性,以下使用Auto_Storage类
验证属性,使用Validated验证doc是否为空字符串,weight、price属性是否为非负值,doc与weight、price均属于验证,具体怎么验证交由负责各自的验证类去处理故而此处采用了模板方法设计模式:一个模板方法用一些抽象的操作定义一个算法,而子类将重定义这些操作以提供具体的行为,所以我们需要用到抽象类(abc.ABC)
代码实现
import abc
class Auto_Storage():
__counter = 0
def __init__(self):
cls = self.__class__
name = cls.__name__
self.storagename = '_{}#{}'.format(name,cls.__counter)
cls.__counter += 1
def __get__(self, instance, owner):
return getattr(instance,self.storagename) #返回一个value
def __set__(self, instance, value):
setattr(instance,self.storagename,value) # setattr 相当于表达式:instance.__dict__[self.storagename] =value
# instance 均代表LineItem类的实例
#抽象类--模板,无法创建实例(继承自ABC抽象类)
class Validated(abc.ABC,Auto_Storage):
def __set__(self, instance, value):
value1 = self.validate(value) #这里self.validate 方法引用 需要在set value之前对value进行验证,所以我们需要重写__set__方法
super().__set__(instance,value1)
#定义抽象方法,有子类quantity、NoBlank内的方法完成具体实现
@abc.abstractmethod
def validate(self,value): #对于validate 验证数据,在Validate中__set__方法覆盖父类方法,其中已经引入instance,故而此处只需要输入value参数就可以了
"# 验证或者判断值是否大于0 写抽象办法"
#具体实现验证非负值
class quantity(Validated): #get set分别继承自Auto_Storage 类、Validated类 --覆盖描述符
def validate(self,value):
if value>0:
return value
else:
raise ValueError('Value must > 0')
#具体实现验证空字符
class NoBlank(Validated): #get set分别继承自Auto_Storage 类、Validated类--覆盖描述符
def validate(self,value):
if len(value) == 0:
raise ValueError('The doc must not be empty')
else:
return value
class LineItem():
doc = NoBlank()
weight = quantity()
price = quantity()
def __init__(self,doc,weight,price):
self.doc = doc
self.weight = weight
self.price = price
def subtotal(self):
return self.doc*self.weight*self.price
demo =LineItem('lll',20,30)
主要继承关系如下:
类属性与继承类:
类属性与继承:在Auto_Storage类中,__counter作为其类属性,为所有该类实例共享,但是在本例中quantity、NoBlank均继承自类Auto_Storage,届时会存在quantity、NoBlank两类实例,类属性:_Auto_Storage__counter 是否发生了 变化,发生了哪些变化?
class Auto_Storage():
__counter = 0 # 引用名称为:_Auto_Storage____counter
def __init__(self):
cls = self.__class__
name = cls.__name__
print(name)
self.storagename = '_{}##{}'.format(name,cls.__counter)
cls.__counter += 1
print( self.storagename)
class demo(Auto_Storage):
pass
class de(Auto_Storage):
pass
c = demo()
a = de()
c = demo()
c = demo()
图示如下:类demo、de、Auto_Storage各自拥有名为父类原始名称:_Auto_Storage____counter的类变量,所在上述实现代码中 quantity类与NoBlank类是独立进行类属性计数