简体   繁体   English

如何在 python 中对 dict 使用点符号?

[英]How to use dot notation for dict in python?

I'm very new to python and I wish I could do .我是 python 的新手,我希望我能做到. notation to access values of a dict .访问dict值的符号。

Lets say I have test like this:可以说我有这样的test

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

But I wish I could do test.name to get value .但我希望我可以做test.name来获得value Infact I did it by overriding the __getattr__ method in my class like this:事实上,我是通过重写 class 中的__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')

and this works: when I do:这有效:当我这样做时:

test.name // I get value.

But the problem is when I just print test alone I get the error as:但问题是当我单独打印test时,我得到的错误是:

'Sorry no key matches'

Why is this happening?为什么会这样?

This functionality already exists in the standard libraries , so I recommend you just use their class.此功能已存在于标准库中,因此我建议您只使用它们的类。

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

Adding, modifying and removing values is achieved with regular attribute access, ie you can use statements like n.key = val and del n.key .添加、修改和删除值是通过常规属性访问实现的,即您可以使用诸如n.key = valdel n.key类的语句。

To go back to a dict again:再次回到字典:

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

The keys in your dict should be string identifiers for attribute access to work properly. dict 中的键应该是字符串标识符,以便属性访问正常工作。

Simple namespace was added in Python 3.3.在 Python 3.3 中添加了简单命名空间。 For older versions of the language, argparse.Namespace has similar behaviour.对于旧版本的语言, argparse.Namespace具有类似的行为。

I assume that you are comfortable in Javascript and want to borrow that kind of syntax... I can tell you by personal experience that this is not a great idea.我假设您对 Javascript 很熟悉并想借用这种语法……我可以根据个人经验告诉您,这不是一个好主意。

It sure does look less verbose and neat;它确实看起来不那么冗长和整洁; but in the long run it is just obscure.但从长远来看,它只是晦涩难懂。 Dicts are dicts, and trying to make them behave like objects with attributes will probably lead to (bad) surprises.字典就是字典,试图让它们表现得像具有属性的对象可能会导致(坏的)意外。

If you need to manipulate the fields of an object as if they were a dictionary, you can always resort to use the internal __dict__ attribute when you need it, and then it is explicitly clear what you are doing.如果您需要像操作字典一样操作对象的字段,您可以随时在需要时使用内部的__dict__属性,然后清楚地知道您在做什么。 Or use getattr(obj, 'key') to have into account the inheritance structure and class attributes too.或者使用getattr(obj, 'key')来考虑继承结构和类属性。

But by reading your example it seems that you are trying something different... As the dot operator will already look in the __dict__ attribute without any extra code.但是通过阅读您的示例,您似乎正在尝试不同的东西......因为点运算符已经在__dict__属性中查找而无需任何额外代码。

In addition to this answer , one can add support for nested dicts as well:除了这个答案之外,还可以添加对嵌套字典的支持:

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

Note that this does not support dot notation for dicts that are somewhere inside eg lists.请注意,这不支持列表中某处的字典的点表示法。

Could you use a named tuple?你可以使用命名元组吗?

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__ is used as a fallback when all other attribute lookup rules have failed.当所有其他属性查找规则都失败时, __getattr__用作后备。 When you try to "print" your object, Python look for a __repr__ method, and since you don't implement it in your class it ends up calling __getattr__ (yes, in Python methods are attributes too).当你尝试“打印”你的对象时,Python 会寻找一个__repr__方法,并且由于你没有在你的类中实现它,它最终会调用__getattr__ (是的,在 Python 中方法也是属性)。 You shouldn't assume which key getattr will be called with, and, most important, __getattr__ must raise an AttributeError if it cannot resolve key .您不应该假设将使用哪个键getattr来调用,最重要的是,如果__getattr__无法解析key ,它必须引发 AttributeError 。

As a side note: don't use self.__dict__ for ordinary attribute access, just use the plain attribute notation:作为旁注:不要使用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)

Now if your class has no other responsability (and your Python version is >= 2.6 and you don't need to support older versions), you may just use a namedtuple : http://docs.python.org/2/library/collections.html#collections.namedtuple现在,如果您的班级没有其他责任(并且您的 Python 版本 >= 2.6,并且您不需要支持旧版本),您可以只使用命名元组:http: //docs.python.org/2/library/ collections.html#collections.namedtuple

You can use the built-in method argparse.Namespace() :您可以使用内置方法argparse.Namespace()

import argparse

args = argparse.Namespace()
args.name = 'value'

print(args.name)
# 'value'

You can also get the original dict via vars(args) .您还可以通过vars(args)获取原始字典。

You have to be careful when using __getattr__ , because it's used for a lot of builtin Python functionality.使用__getattr__时必须小心,因为它用于许多内置的 Python 功能。

Try something like this...尝试这样的事情......

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>

Here is a simple, handy dot notation helper example that is working with nested items:这是一个使用嵌套项的简单、方便的点表示法帮助器示例:

def dict_get(data:dict, path:str, default = None):
    pathList = re.split(r'\.', path, flags=re.IGNORECASE)
    result = data
    for key in pathList:
        try:
            key = int(key) if key.isnumeric() else key 
            result = result[key]
        except:
            result = default
            break
    
    return result

Usage example:使用示例:

my_dict = {"test1": "str1", "nested_dict": {"test2": "str2"}, "nested_list": ["str3", {"test4": "str4"}]}
print(dict_get(my_dict, "test1"))
# str1
print(dict_get(my_dict, "nested_dict.test2"))
# str2
print(dict_get(my_dict, "nested_list.1.test4"))
# str4
class convert_to_dot_notation(dict):
    """
    Access dictionary attributes via dot notation
    """

    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__


test = {"name": "value"}
data = convert_to_dot_notation(test)
print(data.name)

With a small addition to this answer you can support lists as well:这个答案做一点补充,你也可以支持列表:

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))
        elif isinstance(value, list):
            self.__setattr__(key, map(NestedNamespace, value))
        else:
            self.__setattr__(key, value)

I use the dotted_dict package:我使用dotted_dict包:

>>> from dotted_dict import DottedDict
>>> test = DottedDict()
>>> test.name = 'value'
>>> print(test.name)
value

Add a __repr__() method to the class so that you can customize the text to be shown on向类添加__repr__()方法,以便您可以自定义要显示的文本

print text

Learn more here: https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html在这里了解更多信息: https ://web.archive.org/web/20121022015531/http://diveintopython.net/object_orient_framework/special_class_methods2.html

#!/usr/bin/env python3


import json
from sklearn.utils import Bunch
from collections.abc import MutableMapping


def dotted(inpt: MutableMapping,
           *args,
           **kwargs
           ) -> Bunch:
    """
    Enables recursive dot notation for ``dict``.
    """

    return json.loads(json.dumps(inpt),
                      object_hook=lambda x:
                      Bunch(**{**Bunch(), **x}))

2022 answer: I've created the dotwiz package -- this is a fast, tiny library that seems to perform really well in most cases. 2022 回答:我创建了dotwiz包——这是一个快速、小巧的库,在大多数情况下似乎表现得非常好。

>>> from dotwiz import DotWiz
>>> test = DotWiz(hello='world')
>>> test.works = True
>>> test
✫(hello='world', works=True)
>>> test.hello
'world'
>>> assert test.works

This feature is baked into OmegaConf :此功能已嵌入到OmegaConf 中

from omegaconf import OmegaConf

your_dict = {"k" : "v", "list" : [1, {"a": "1", "b": "2", 3: "c"}]}
adot_dict = OmegaConf.create(your_dict)

print(adot_dict.k)
print(adot_dict.list)

Installation is:安装是:

pip install omegaconf

This lib comes in handy for configurations, which it is actually made for:这个库对于配置很方便,它实际上是为:

from omegaconf import OmegaConf
cfg = OmegaConf.load('config.yml')
print(cfg.data_path)

You can make hacks adding dot notation to Dicts mostly work, but there are always namespace problems.您可以使向 Dicts 添加点符号的 hacks 大部分工作,但总是存在名称空间问题。 As in, what does this do?比如,这是做什么的?

x = DotDict()
x["values"] = 1989
print(x. values)

I use pydash , which is a Python port of JS's lodash, to do these things a different way when the nesting gets too ugly.我使用pydash ,它是 JS 的 lodash 的 Python 端口,当嵌套变得太丑陋时,我会以不同的方式做这些事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM