Python descriptor

只要是定义了__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)

表面看,上面这种做法更麻烦了。但考虑到重用性,这种方式更合适。 针对属性定一个类,这样类可以重用,且不同属性可以定制不同的验证规则。

上一篇:Es 之must,should filter

下一篇:How to set element-ui table header height