前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 黑魔法之属性拦截

Python 黑魔法之属性拦截

作者头像
编程文青李狗蛋
发布2019-11-07 15:41:50
5960
发布2019-11-07 15:41:50
举报
本文字数:2075 字

阅读本文大概需要:6 分钟

写在之前

我们在昨天的文章(Python 黑魔法之内存优化)讲了一种黑魔法,今天我们来讲另一种:「属性拦截」。当我们访问某个类或者是实例属性的时候,如果它不存在的话,就会出现异常。对于异常,我们总是要处理的,接下来就让我们来看,是怎么处理的。

属性拦截

在 Python 中,就有一些具有拦截功能的方法,让我们先来看一下:

1.__setattr__(self,name,value):如果想要给 name 赋值的话,就需要调用这个方法。

2.__getattr__(self,name):如果 name 被访问且它又不存在,那么这个方法将被调用。

3.__delattr__(self,name):如果要删除 name 的话,这个方法就要被调用了。

下面我们用例子来演示一下:

代码语言:javascript
复制
>>> class Sample:
...     def __getattr__(self,name):
...             print('hello getattr')
...     def __setattr__(self,name,value):
...             print('hello setattr')
...             self.__dict__[name] = value
...

上面的例子中类 Sample 只有两个方法,下面让我们实例化一下:

代码语言:javascript
复制
>>> s = Sample()
>>> s.x
hello getattr

s.x 这个实例属性本来是不存在的,但是由于类中有了 __getattr__(self,name) 方法,当发现属性 x 不存在于对象的 __dict__ 中时,就调用了 __getattr__,也就是所谓的「拦截成员」。

代码语言:javascript
复制
>>> s.x = 7
hello setattr

同理,给对象的属性赋值的时候,调用了 __setattr__(self,name,value) 方法,这个方法中有 self.__dict__[name] = value,通过这个就将属性和数据存到了对象 __dict__ 中。如果再调用这个属性的话,会成为下面这样:

代码语言:javascript
复制
>>> s.x
7

出现这种结果的原因是它已经存在于对象的 __dict__ 中了。

看了上面的两个,你是不是觉得上面的方法很有魔力呢?这就是「黑魔法」,但是它具体有什么应用呢?我们接着来看:

代码语言:javascript
复制
class Rectangle:
    """
    the width and length of Rectangle
    """

    def __init__(self):
        self.width = 0
        self.length = 0

    def setSize(self,size):
        self.width, self.length = size

    def getSize(self):
        return self.width, self.length

if __name__ == "__main__":
    r = Rectangle()
    r.width = 3
    r.length = 4
    print(r.getSize())
    print(r.setSize((30,40)))
    print(r.width)
    print(r.length)

上面是我根据一个很有名的例子改编的,你可以先想一下结果,想完以后可以接着往下看:

代码语言:javascript
复制
(3, 4)
30
40

这段代码运行的结果如上面所示,作为一个强迫证的码农,对于这种可以改进的程序当然不能容忍。我们在上面介绍的特殊方法,我们一定要学以致用,虽然重新写的不一定比原来的好,但我们还是要尝试去用:

代码语言:javascript
复制
class NewRectangle:
    """
    the width and length of Rectangle
    """

    def __init__(self):
        self.width = 0
        self.length = 0

    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.length = value
        else:
            self.__dict__[name] = value

    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.length
        else:
            return AttributeError

if __name__ == "__main__":
    r = NewRectangle()
    r.width = 3
    r.length = 4
    print(r.size)
    r.size = 30,40
    print(r.width)
    print(r.length)

我们来看一下运行的结果:

代码语言:javascript
复制
(3, 4)
30
40

我们可以看到,除了类的写法变了以外,调用的方式没有变,结果也没有变。

写在之后

黑魔法我暂时先说这两个,其实还有别的,如果你感兴趣的话,可以去 Google 一些文章来看,相信自己的探索会让自己收获更多。

至此,不知道你发现了没有,我们使用了很多以双下划线开头和结尾的属性和方法,比如 __init__ 等。在 Python 中,以双下划线开头和结尾的都是「特殊」的,当然这个只是一个惯例,目的是为了确保这些特殊的名字不会和你定义的名字发生冲突。如果你看着它们不顺眼,想要把它们重写当然也是可以的。

如果你觉得本篇文章对你有所帮助的话,欢迎点赞转发,让更多的人看到,期待和你的交流。

The end。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python空间 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档