目录
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
'''
这样,‘ . ’访问通过[ ]访问也间接地达到了访问属性的效果。