关于字典:如何在python中对dict使用点符号?

How to use dot notation for dict in python?

我对python非常陌生,我希望我可以用.表示法来访问dict的值。

假设我有一个这样的test

1
2
3
4
>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value

但我希望我能做test.name来得到value。实际上,我是这样做的:在我的类中重写__getattr__方法:

1
2
3
4
5
6
7
8
9
10
class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self,key):
        try:
            return self._response[key]
        except KeyError,err:
            sys.stderr.write('Sorry no key matches')

这是可行的!当我这样做的时候:

1
test.name // I get value.

但问题是,当我单独打印test时,错误如下:

1
'Sorry no key matches'

为什么会这样?


这个功能已经存在于标准库中,所以我建议您只使用它们的类。

1
2
3
4
5
6
7
>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'

添加、修改和删除值是通过常规属性访问实现的,也就是说,您可以使用诸如n.key = valdel n.key之类的语句。

再次回到听写:

1
2
>>> vars(n)
{'key1': 'value1', 'key2': 'value2'}

dict中的键应该是字符串标识符,以便属性访问正常工作。

在python 3.3中添加了简单的名称空间。对于旧版本的语言,argparse.Namespace具有类似的行为。


我假设您对JavaScript很熟悉,并且希望借用这种语法…我可以根据个人经验告诉你,这不是一个好主意。

它看起来确实不那么冗长和整洁;但从长远来看,它只是模糊的。dict是dict,试图让它们像具有属性的对象一样工作可能会导致(坏的)意外。

如果您需要像处理字典一样处理对象的字段,那么在需要时,您可以始终使用内部__dict__属性,然后明确说明您在做什么。或者使用getattr(obj, 'key')来考虑继承结构和类属性。

但是通过阅读你的例子,你似乎在尝试一些不同的东西…因为点运算符已经在没有任何额外代码的情况下查找__dict__属性。


当所有其他属性查找规则都失败时,使用__getattr__作为回退。当你试图"打印"你的对象时,python会寻找一个__repr__方法,由于你没有在你的类中实现它,它最终会调用__getattr__(是的,在python方法中也是属性)。您不应该假定调用哪个getattr键,最重要的是,如果无法解决key__getattr__必须引发attributeerRor。

附带说明:普通属性访问不使用self.__dict__,只使用普通属性表示法:

1
2
3
4
5
6
7
8
9
10
11
class JuspayObject:

    def __init__(self,response):
        # don't use self.__dict__ here
        self._response = response

    def __getattr__(self,key):
        try:
            return self._response[key]
        except KeyError,err:
            raise AttributeError(key)

现在,如果您的类没有其他的职责(并且您的Python版本大于等于2.6,并且您不需要支持旧版本),您可以只使用一个namedtuple:http://docs.python.org/2/library/collections.html collections.namedtuple


你能用一个命名的元组吗?

1
2
3
4
5
from collections import namedtuple
Test = namedtuple('Test', 'name foo bar')
my_test = Test('value', 'foo_val', 'bar_val')
print(my_test)
print(my_test.name)


使用__getattr__时必须小心,因为它用于许多内置的python功能。

尝试这样的方法…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self, key):
        # First, try to return from _response
        try:
            return self.__dict__['_response'][key]
        except KeyError:
            pass
        # If that fails, return default behavior so we don't break Python
        try:
            return self.__dict__[key]
        except KeyError:
            raise AttributeError, key

>>> j = JuspayObject({'foo': 'bar'})
>>> j.foo
'bar'
>>> j
<__main__.JuspayObject instance at 0x7fbdd55965f0>


除此之外,还可以添加对嵌套dict的支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from types import SimpleNamespace

class NestedNamespace(SimpleNamespace):
    def __init__(self, dictionary, **kwargs):
        super().__init__(**kwargs)
        for key, value in dictionary.items():
            if isinstance(value, dict):
                self.__setattr__(key, NestedNamespace(value))
            else:
                self.__setattr__(key, value)

nested_namespace = NestedNamespace({
    'parent': {
        'child': {
            'grandchild': 'value'
        }
    },
    'normal_key': 'normal value',
})


print(nested_namespace.parent.child.grandchild)  # value
print(nested_namespace.normal_key)  # normal value


向类中添加__repr__()方法,以便自定义要显示的文本

1
print text

更多信息请访问:http://www.diveintopython.net/object-oriented-framework/special-class-methods2.html