python中的描述符

python中描述符,也属于比较底层的一个特性。 查看了很多资料终于知道它是干啥的了。

描述符的作用是实现对属性的代理。

 

如何进行属性验证?

举例来说,如果你想你的类有个属性,age。 对于年龄来说,我们认为它的有效范围应该是0-120。那么怎么限制属性的值必须在这个范围呢?

首先,我们可以在__init__中进行限制,但是这里只是限制了初始化时,age的值必须有效

class Desc(object):

    def __init__(self, age):
        if 0 <= age <= 120:
            self.age = age
        else:
            self.age = 0

但是上面的实现方式并不完美,因为我们可以通过实例 x.age = 1111 方式给属性赋非法值。

我们继续更新代码如下:

class Desc(object):

    def __init__(self, age):
        if 0 <= age <= 120:
            self._age = age
        else:
            self._age = 0

    def set_age(self,age):
        if 0 <= age <= 120:
            self._age = age
        else:
            raise ValueError("invalid age")

    def get_age(self):
        return self._age


    age = property(get_age,set_age)

这次完美了吧,无论初始化还是属性操作都能进行检查了。

但是,这里有个问题,如果我有很多个相似的属性需要检查,譬如一个学生类,它拥有10门成绩,每个成绩都是一个属性。 那么将产生大量的重复性代码。

 

如果解决这类的问题呢,这里就要用到描述符了。

 

 

 

描述符是什么呢?

描述符(descriptor)实际是一种类,但是这个类实现了__set__ __get__ __delete__ 这3个方法中一个或多个。 其中get是必须的,其他2个可选。

如果实现中实现了__set__ __get__那么称之为数据描述符

如果只实现了 __get__ 那么称之为非数据描述符

 

 

怎么应用描述符呢?

要想使用描述符,要将一个描述符类的实例赋值给一个托管类(或叫用户类)的一个类属性, 此后无论是通过托管类的类还是实例来访问这个属性时,都会自动调用描述符对象中的3个函数。

 

下面的列子中,DataDesc是个描述符,托管类时TestDesc,需要托管的参数包括:name,age,sex,需要进行检查的是,赋值给属性时,要使用规定的类型,譬如age必须是整形。

# -*- coding:utf8 -*-

from weakref import WeakKeyDictionary

# 数据描述符
class DataDesc(object):


    def __init__(self, propertyName, propertyType):
        """
        :param propertyName:  要代理的属性的名字
        :param propertyType:  要代理的属性的类型
        """
        self._name = propertyName
        self._type = propertyType

        # 我们可以把属性存储在这里,也可存储在instance.__dict__中
        self._data = WeakKeyDictionary()

    def __get__(self, instance, owner):
        # __get__ 要么返回属性值,要么抛出异常
        print "__get__ called, return the value: %s" % str(self._data[instance])
        return self._data[instance]

    def __set__(self, instance, value):
        # 我们可以在这里对value的类型和范围仅限限定,以此实现对属性的控制

        if isinstance(value, self._type):
            print "__set__ called, got a new value: %s" % str(value)
            self._data[instance] = value
        else:
            raise ValueError("The value is not the %s" % type(self._type))

    def __delete__(self, instance):
        del self._data[instance]

class TestDesc(object):

    # 必须在类属性中使用描述符,否则不会调用描述符类的__set__, __get__, __delete__
    name = DataDesc("name", str)
    age = DataDesc("age", int)
    sex = DataDesc("gender", bool)

    def __init__(self, name, age, sex):

        # 这里的赋值操作已经在调用描述符的set函数了
        self.name = name
        self.age = age
        self.sex = sex


td1 = TestDesc("liming", 18, True)
td2 = TestDesc("dasha", 22, False)

print td1.name
print td1.age
print td1.sex

print td2.name
print td2.age
print td2.sex

运行上面的代码,它的输出:

__set__ called, got a new value: liming
__set__ called, got a new value: 18
__set__ called, got a new value: True
__set__ called, got a new value: dasha
__set__ called, got a new value: 22
__set__ called, got a new value: False
__get__ called, return the value: liming
liming
__get__ called, return the value: 18
18
__get__ called, return the value: True
True
__get__ called, return the value: dasha
dasha
__get__ called, return the value: 22
22
__get__ called, return the value: False
False

 

数据描述符和非数据描述符有什么不同吗?

1. 没有set函数,无法写属性

2. 属性查找属性的不同:操作一个属性时,先要验证这个属性是不是这个实例的类的数据描述符。 如果不是,那么检查属性是否能在实例的__dict__中找到。 如果找不到,那们将会检查这个属性是不是这个实例的类的非数据描述符   

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值