如何在Python中使用点符号表示字典?


77

我是python的新手,我希望我能做 .符号来访问a的值dict

可以说我有 test这样:

>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value

但是,我希望我能做到test.namevalue。实际上,我是通过覆盖__getattr__类中方法来一点的:

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')

这有效!当我做:

test.name // I get value.

但是问题是我什么时候打印 test时,出现以下错误:

'Sorry no key matches'

为什么会这样呢?


当您请求不在字典中的属性时,需要调用超类getattr
David Heffernan

@DavidHeffernan是否像OP的示例这样的老式类甚至具有超类?
2013年

@A不知道。如果不是,请使用新的样式类。谁仍然使用旧样式类?
David Heffernan

1
@DavidHeffernan嗯,标准Python库中仍然有很多老式类,例如cgi.py
2013年

Answers:


142

标准库中已经存在此功能,因此我建议您只使用它们的类。

>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'

添加,修改和删除值是通过常规属性访问实现的,即,您可以使用诸如n.key = val和的语句del n.key

要再次返回字典:

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

字典中的键应为字符串标识符,以便属性访问才能正常工作。

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


2
Fabric也有一个很好的最小实现,我也在这里发布了它
戴夫

4
如果您可以递归的话,那就太好了。即访问myobj.subdict.subdict
Pithikos '18

2
@Pithikos有一个第三方库提供该功能,请检出python-box
威姆

很酷,但不适用于嵌套字典...
eadmaster

@eadmaster您是否在您的正上方阅读了两条评论?
维姆

41

我假设您熟悉Java语言,并且想借用这种语法...我可以凭个人经验告诉您,这不是一个好主意。

它看起来确实不那么冗长和整洁。但从长远来看,它只是晦涩难懂。字典是字典,试图使它们的行为像带有属性的对象一样,可能会导致(严重)意外。

如果需要像处理字典一样操作对象的字段,则始终可以__dict__在需要时使用内部属性,这样就可以清楚地知道正在做什么。或getattr(obj, 'key')也用于考虑继承结构和类属性。

但是通过阅读示例,您似乎正在尝试一些不同的方法...点运算符已经可以在__dict__属性中查找内容,而无需任何额外的代码。


10
令人惊讶的例子:如果dict中有一个键恰好是python关键字,例如string 'for',那么属性访问将失败,并且没有优雅的方法可以正确处理这种情况。整个想法从一开始就被打破了。
2013年

4
另一方面,自动完成功能使代码更高效,更不易出错。如果可以预定义dict结构,
圆形的

7

您可以使用一个命名的元组吗?

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)


2
这是一种通过点分表示法访问数据结构的有趣方式,但是它似乎与JSON或dict并不特别兼容。有一些使用命名元组的库确实提供了JSON和dict支持。尝试github.com/dsc/bunchgithub.com/kennknowles/python-jsonpath-rw
MarkHu '16

对于python> = 3.6,请考虑from typing import NamedTuple
user9074332 '19

6

除了这个答案之外,还可以添加对嵌套字典的支持:

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

请注意,这不支持点表示法,例如列表内的字典。


5

__getattr__当所有其他属性查找规则均失败时,用作备用。当您尝试“打印”对象时,Python会寻找一种__repr__方法,并且由于您没有在类中实现它,因此最终会调用__getattr__(是的,在Python中方法也是属性)。您不应该假定将与哪个键一起调用getattr,最重要的是,__getattr__如果无法解析,则必须引发AttributeError key

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

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


2

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

试试这样的东西...

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>

1
__getattr__仅当没有其他查找规则匹配时才用作备用,因此不需要查找self.__dict__[key],如果__getattr__已调用,则已经完成。AttributeError如果对self._response ['key']的查找失败,只需提高就可以了。
bruno desthuilliers 2013年

@brunodesthuilliers看起来您是对的。奇怪。也许仅在Python v1.5时代才需要这样做。
ya

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.