Python:利用dict继承编写自定义Dict类

目录

前言

探究思路及解决方法

1.为什么dict可以被继承

2.super().__init__(**kw)是什么

3.__getattr__和__setattr__的具体作用是什么

代码分析


前言

最近在学习python中,对利用dict继承编写自定义Dict类方面产生一些问题。问题源于廖雪峰老师博客中的一个问题:编写一个Dict类,要求类的行为和dict一致,但是可以通过属性来访问。文章将通过探究思路和代码分析解决这个问题。个人理解如有错误还请不吝赐教。 

探究思路及解决方法

原文实现代码如下:

class Dict(dict):

    def __init__(self, **kw):
        super().__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

在初见代码时,作为一个初学者,我提出了以下问题:

1.为什么dict可以被继承

由于初次接触面对对象编程,对数据类型和类的关系了解还不够透彻,所以对括号中的dict提出质疑。但由继承的定义可知:继承允许我们定义继承另一个类的所有方法和属性的类。所以初步猜测继承的对象dict是类。既然是类,那么就应该有定义,在IDE中右键dict跳转至定义,发现 class dict() 我初步的假设似乎被验证了。但在查询相关资料后发现,其实这种说法还是不够恰当,更明确的说:dict和类都同属于数据结构,而类则是一种特殊的,可自定义的数据结构。

2.super().__init__(**kw)是什么

对这行语句的疑问主要体现在super()和__init__上。相对轻松,经过查询得知:super()的作用是调用继承的函数即父类,后接的则是对应的方法。而由于是调用,所以参数也省略了self,直接用关键字参数传入任意键值对。

另外需要了解的是,__xxx__作为定制类的特殊方法,本质还是方法,所以可以被调用。如在super()后被调用,在没有特殊需求时,这些特殊方法保持默认,而在有特殊需求时则可以重定义。

3.__getattr__和__setattr__的具体作用是什么

getattr和setattr的字面意思是获取属性和设置属性,抛开dict父类,先探究这两个特殊方法的作用。根据字面意思,初步判断是object属性的输入和输出。以setattr为例,参考资料其作用是:当访问未定义的属性时,python解释器试图调用此方法来尝试获取属性。遂用以下代码进行验证:

class Dict(object):
    def __setattr__(self,key,value):
        print('This is __setattr__')  #判断是否跳转至此函数
        super().__setattr__(key,value)    #注意直接使用self.key = value会导致程序进入死循环

text = Dict()
text.a = 1

'''
运行结果为:
This is __setattr__
1
'''

验证成功,所以__getattr__和__setattr__方法的作用就是获取和设置未定义的属性。在默认设置中,__getattr__会对未定义的属性抛出异常;__setattr__会定义未定义的属性并赋值。

至此,迷雾似乎已经拨开,答案已经跃然纸上了。

代码分析

class Dict(dict):

    def __init__(self, **kw):
        super().__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

分析需求,我们需要编写一个Dict类,要求类的行为和dict一致,但是可以通过属性来访问。用通俗的话来说,就是Dict既要实现[ ]访问属性,也要实现‘ . ’访问属性。[ ]访问属性的方法在dict中已被封装,不需要我们操心,所以我们将目光聚集向通过‘ . ’访问属性。

回顾继承的定义,所以当前Dict拥有了dict的属性方法以及作为类Dict默认的方法。相同的方法会以子类为准,看似Dict的__init__覆盖了dict,但其实我们发现Dict调用的还是dict中的__init__,所以一个方法的重定义只是为了强调,可以删除。而通过__getattr__和__setattr__则可以很巧妙地完成‘ . ’访问的需求。如输入text.a = 1,将跳转至__setattr__,执行方法内的[ ]访问达成输入目的;输入print(text.a),由于Dict内其实并未创建a属性(创建的是dict的a属性),跳转至__getattr__尝试用[ ]返回属性,失败则抛出异常。

text = Dict()

text.a = 1  
print(text.a)                #'.'访问属性

text['b'] = 2
print(text['b'])             #[ ]访问属性

'''
运行结果为:
1
2
'''

这样,‘ . ’访问通过[ ]访问也间接地达到了访问属性的效果。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值