只要是定义了__get__()、set()、__delete()__中任意一个方法的对象都叫描述符 通常情况下,类P的属性x,我们获取时obj.x 是从 p.__dict__中取值,而如果定义了相应的描述符,就可以改变这种行为.
如果我们要对类的某种行为做一定的限制,下面的方式可以达到目的,但这种方式显然是不合适的。
class Person:
def __init__(self, name, age):
self.name = name
if not isinstance(age, int):
raise TypeError('Expected an int!')
self.age = age
p = Person('andy', 10)
print(p.name, p.age)
p1 = Person('andy', '10')
print(p1.name, p1.age)
下面用property装饰器来实现:
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a str!')
self.__name = value
@name.deleter
def name(self):
del self.__name
p = Person('andy')
print(p.name)
p.name = 10 # TypeError: Expected a str!
print(p.name)
假如我们想在对类属性进行操作时,对它做一定的限制,比如类型检查,可以用上面的方式实现。 但通常情况下,类有多个属性,不太合适每个属性都这么写一遍,那有没有更好的方法呢?
class String:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
class Integers:
def __init__(self, age):
self.age = age
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.age]
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Expected an int')
instance.__dict__[self.age] = value
def __delete__(self, instance):
del instance.__dict__[self.age]
class Person:
name = String('name')
age = Integers('age')
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('andy', 10)
print(p.name, p.age)
p.name = 10 # TypeError: Expected a string
print(p.name)
表面看,上面这种做法更麻烦了。但考虑到重用性,这种方式更合适。 针对属性定一个类,这样类可以重用,且不同属性可以定制不同的验证规则。