Python 重写字典访问方法
我有一个叫 devices
的类,它是从字典类(dict)继承来的。这个 devices
就是一个字典里面又包含字典的结构。我想通过方法调用的方式来访问它的键和值,同时也希望能用普通的方式来访问。也就是说,像这样:
class Devices(dict):
def __init__(self):
self['device'] = {'name':'device_name'}
def __getattr__(self, name):
value = self[name]
return value
...
mydevices = Devices()
mydevice = mydevices.device #works and mydevices['device'] also works
#But what code do I need for the following?
name = mydevices.device.name
mydevices.device.name = 'another_name'
我知道如果我重写 __getattr__
和 __setattr__
这两个方法,我就可以做到这一点。但是从我的代码来看,我不太确定怎么去访问嵌套的字典。有没有人知道怎么实现这个?
谢谢,labjunky
3 个回答
一般来说,要访问字典中的值,应该使用 dictionary[key]
而不是 dictionary.key
。在第一种情况下,使用属性的方式之所以有效,是因为你重写了 __getattr__
,而不是 __getitem__
。这就是为什么它只对你的类有效,而对内部字典无效。
现在,要在 __getitem__
中访问字典(__getattr__
是用来访问属性的,比如 dict.keys
,其中 keys
是属性)时,你需要小心一点。你需要调用字典类的 __getitem__
方法(否则会导致无限递归)。有两种方法可以做到这一点。第一种方法在你选择改变对象的继承时会比较持久:
def __getitem__(self, name):
value = super(Devices,self).__getitem__(name)
return value
另一种方法是:
def __getitem__(self, name):
value = dict.__getitem__(self, name)
return value
如果你仍然想用属性来访问值,那么每次使用字典时,你都需要用你的类来替换字典(所以 self['device'] = {'name':'device_name'}
将变成 self['device'] = MyDictClass({'name':'device_name'})
)。
顺便提一下:注意你在 __init__
中有个错误。self = {'device':{'name':'device_name'}}
这一行实际上没有任何作用。(它把字典放在了局部变量 self
中,但这个变量在你退出函数后就消失了。)
你应该使用能改变对象本身的操作。也就是说:
def __init__(self):
self['device'] = {'name':'device_name'}
你可以使用这个有点特别的类,它是 dict
的一个子类,你可以通过两种方式来访问它的键:一种是像平常那样用 d[k]
,另一种是用属性的方式 d.k
。
class Struct(dict):
def __init__(self,*args, **kwargs):
dict.__init__(self,*args, **kwargs)
self.__dict__ = self # yes, it is tricky
这里的关键是,解释器在调用 __[gs]etattr__
之前会先检查 __dict__
,所以你甚至不需要去重写这些方法。
现在你可以这样做:
class Devices(Struct):
pass
devices = Devices()
devices.device = Struct(name='devicename')
devices.device.name = 'anothername'
所以你的回答基本上已经很完整了。你可以用下面的方式来定义你想要的结构:
class Devices(dict):
def __init__(self,inpt={}):
super(Devices,self).__init__(inpt)
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self,name,value):
self.__setitem__(name,value)
然后一个使用案例可以是:
In [6]: x = Devices()
In [7]: x.devices = Devices({"name":"thom"})
In [8]: x.devices.name
Out[8]: 'thom'
基本上就是把你的属性查找字典嵌套在一起。