公众号:pythonislover
Python大数据与SQL优化笔记
我们都知道Python是一个弱类型语言,就是变量,参数无需自己定义类型,用过JAVA的同学应该知道两者之间的区别。下面我们看一个例子
class Person:
def __init__(self,name,age,weight):
self.name = name
self.age = age
self.weight = weight
lei = Person('李雷',18,173)
ming = Person('小明','19','180cm')
print('我叫%s,我今年%s岁,身高%s' %(lei.name,lei.age,lei.weight))
print('我叫%s,我今年%s岁,身高%s' %(ming.name,ming.age,ming.weight))
结果:
我叫李雷,我今年18岁,身高173
我叫小明,我今年19岁,身高180cm
我们看看按道理说name应该是字符串类型,age应该是int类型,weight应该是浮点类型的,但是上面的案例,我们各自参数无论你传入上面样类型的值,都不会报错,正常打印出来。
那今天我们要讲的就是使用python中的描述符功能实现这些类型的检查,保证name必须是字符串,age必须是int, weight必须是浮点型,如果传入错误的类型,程序会出错。
什么是python的描述符
这边主要是三个内置函数__get__(),__set__(),__delete__()
__get__():调用一个属性时触发
__set__():为属性赋值时触发
__delete__():del删除属性时触发
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个
2. 描述符的种类
1)数据描述符
至少实现了__get__()和__set__()
2)非数据描述符
没有实现__set__()
具体什么意思我们下面来说。
3. 描述符是干什么的
描述符的作用是用来代理另外一个类的属性的,实现在实例调用,设置,删除属性之前,触发我们的代理,从而触发描述符中的__get__(),__set__(),__delete__()方法
4.优先级
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
下面我们就具体代码例子用数据描述符实现我们上面要实现的参数类型限制
#Descriptor类其实就是一个描述符,因为至少实现了__get__(),__set__(),__delete__()中的一个
class Descriptor:
def __init__(self,name,expected_type):
self.name = name #代理属性名称
self.expected_type =expected_type #代理属性类型
def __get__(self,instance,value):
print('触发描述符get方法') #触发描述符get方法
# instance参数是:< __main__.Person object at 0x00000176B6C22BA8 > ,Person object其实就像下面实例化的对象“lei”
print('instance参数是:',instance)
# value参数是:
print('value参数是:', value)
# 具体实现get属性的方法
return instance.__dict__[self.name]
def __set__(self,instance,value):
print('触发描述符set方法')
# instance参数是:< __main__.Person object at 0x000001EE8C899080 > , Person object其实就像下面实例化的对象“lei”
# value参数是:李雷
print('instance参数是:', instance)
print('value参数是:', value)
#具体实现set属性的方法,具体实现参数的类型控制
if not isinstance(value,self.expected_type):
raise TypeError('参数 %s 的Expected Type is %s' % (value,self.expected_type))
instance.__dict__[self.name] = value
def __delete__(self,instance):
print('触发描述符delete方法')
print('instance参数是:', instance)
# 具体实现delete属性的方法
instance.__dict__.pop(self.name)
class Person:
#这种方法书写,说明name,age,weight属性被上面的Descriptor描述符代理检查,实例调用属性的时候首先
#触发上面的描述符中的相应get,set,delete方法
#Descriptor描述符其实也是一个类,有自己的构造函数,所以我们传入的是2个参数,一个是代理属性,一个是属性期望的类型
#以下面的name为例子,我们要代理的属性是“name”, 期望name属性的类型是字符型,以此类推
name = Descriptor('name',str)
age = Descriptor('name',int)
weight = Descriptor('name',float)
def __init__(self,name,age,weight):
self.name = name
self.age = age
self.weight = weight
#实例化会触发上面描述符的__set__方法
#因为实例的属性优先级小于描述符的优先级,这里的实例化会首先触发__set__方法,然后实现类型限定
lei = Person('李雷',18,173.0)
#调用属性会触发描述符的__get__方法
print('我叫%s,我今年%s岁,身高%s' %(lei.name,lei.age,lei.weight))
所有的解释说明都在代码里面,大家自行查看,上面我们实例化
lei = Person('李雷',18,173.0)
这完全符合我们设置的参数类型,所有不会报错,但是如果我们传入错误的参数类型,如:
lei = Person('李雷','18',173.0)
结果:
TypeError: 参数 18 的Expected Type is
这里就起到了类型限制的作用。
下面说下非数据描述符,看看有什么不同
#Descriptor类其实就是一个非数据描述符,只实现了__get__方法
class Descriptor:
def __init__(self,name,expected_type):
self.name = name
self.expected_type =expected_type
def __get__(self,instance,value):
print('触发描述符get方法')
print('instance参数是:',instance)
print('value参数是:', value)
return instance.__dict__[self.name]
class Person:
name = Descriptor('name',str)
age = Descriptor('name',int)
weight = Descriptor('name',float)
def __init__(self,name,age,weight):
self.name = name
self.age = age
self.weight = weight
#因为实例的属性优先级大于非数据描述符的优先级,这里的实例化就是在对象的属性字典里面加,和描述符无关
lei = Person('李雷','18',173.0)
print('我叫%s,我今年%s岁,身高%s' %(lei.name,lei.age,lei.weight))
这样和我们一样调用实例化的结果一样了。
这就说明了数据描述符的优先级>实例属性>非数据描述符。
个人意见,望指正