Python面向对象编程 —— 描述器讲解

Time will tell.

前言

前面的Python文章中讲到过,我们可以用@property装饰器将方法包装成属性,这样的属性,相比于其他属性有一个优点就是可以在对属性赋值时,进行变量检查,举例:

class A:
    def __init__(self, name, score):
        self.name = name # 普通属性
        self._score = score
        
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        print('setting score here')
        if isinstance(value, int):
            self._score = value
        else:
            print('please input an int')
        
a = A('Bob',90)
a.name # 'Bob'
a.score # 90
a.name = 1
a.name # 1 ,名字本身不应该允许赋值为数字,但是这里无法控制其赋值
a.score = 83
a.score # 83,当赋值为数值型的时候,可以顺利运行
a.score = 'bob' # please input an int
a.score # 83,赋值为字符串时,score没有被改变


当我们有很多这样的属性时,如果每一个都去使用@property,代码就会过于冗余,如下:

class A:
    def __init__(self, name, score, age):
        self.name = name # 普通属性
        self._score = score
        self._age = age
        
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        print('setting score here')
        if isinstance(value, int):
            self._score = value
        else:
            print('please input an int')
            
    @property
    def age(self):
        return self._age
    
    @age.setter
    def age(self, value):
        print('setting age here')
        if isinstance(value, int):
            self._age = value
        else:
            print('please input an int')
            
a = A('Bob', 90, 20)

因为每次检验的方法都是一样的,所以最好有方法可以批量实现这件事,只写一次 if isinstance 。描述器就可以用来实现这件事。

为了能够更清楚地理解描述器如何实现,我们先跳开这个话题,先讲讲描述器的基本理论。

一、描述器理论及实例

描述器功能强大,应用广泛,它可以控制我们访问属性、方法的行为,是@property、super、静态方法、类方法、甚至属性、实例背后的实现机制,是一种比较底层的设计,因此理解起来也会有一些困难。

定义:从描述器的创建来说,一个类中定义了__get____set____delete__中的一个或几个,这个类的实例就可以叫做一个描述器。

为了能更真切地体会描述器是什么,我们先看一个最简单的例子,这个例子不实现什么功能,只是使用了描述器。

# 创建一个描述器的类,它的实例就是一个描述器
# 这个类要有__get__  __set__ 这样的方法
# 这种类是当做工具使用的,不单独使用
class M:
    def __init__(self, x=1):
        self.x = x
        
    def __get__(self, instance, owner):
        return self.x
    
    def __set__(self, instance, value):
        self.x = value
        
# 调用描述器的类
class AA:
    m = M() # m就是一个描述器
    
aa = AA()
aa.m # 1
aa.m = 2
aa.m # 2


分析上面这个例子:

创建aa实例和普通类没什么区别,我们从aa.m开始看。

aa.m是aa实例调用了m这个类属性,然而这个类属性不是普通的值,而是一个描述器,所以我们从访问这个类属性变成了访问这个描述器。

如果调用时得到的是一个描述器,python内部就会自动触发一套使用机制
访问的话自动触发描述器的__get__方法。

修改设置的话就自动触发描述器的__set__方法。

这里就是aa.m触发了__get__方法,得到的是 self.x 的值,在前面__init__中定义的为1。

aa.m = 2 则触发了__set__方法,赋的值 2 传到 value 参数之中,改变了self.x的值,所以下一次aa.m调用的值也改变了。

进一步思考:当访问一个属性时,我们可以不直接给一个值,而是接一个描述器,让访问和修改设置时自动调用__get__方法和__set__方法。再在__get__方法和__set__方法中进行某种处理,就可以实现更改操作属性行为的目的。这就是描述器做的事情。

相信有的读者已经想到了,开头引言部分的例子,就是用描述器这样实现的。在讲具体如何实现之前,我们要先了解更多关于描述器的调用机制。

二、描述器的调用机制

aa.m命令其实是查找m属性的过程,程序会先到哪里找,没有的话再到哪里找,这是有一个顺序的,说明访问顺序时需要用到__dict__方法。

先看下面的代码了解一下__dict__方法:

class C:
    x = 1
    def __init__(self, y):
        self.y = y
        
    def fun(self):
        print(self.y)
        
c = C(2)
# 实例有哪些属性
print(c.__dict__) # {'y': 2}
# 类有什么属性
print(C.__dict__) # 里面有 x fun
print(type(c).
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值