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

Posted

技术标签:

【中文标题】如何在python中为dict使用点符号?【英文标题】:How to use dot notation for dict in python? 【发布时间】:2013-04-23 03:39:49 【问题描述】:

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

假设我有这样的test

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

但我希望我可以通过test.name 获得value。事实上,我通过在我的类中重写 __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 @DavidHeffernan 像 OP 示例这样的旧式类是否还有超类? @Aya 不知道。如果没有,请使用新的样式类。谁还在使用旧式类? @DavidHeffernan 好吧,标准 Python 库中仍然有很多旧式类,例如cgi.py. 【参考方案1】:

这个功能已经exists in the standard libraries,所以我建议你只使用他们的类。

>>> 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 这样的语句。

再次回到字典:

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

您的字典中的键应该是字符串identifiers,以便属性访问正常工作。

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

【讨论】:

Fabric 也有一个很好的最小implementation,我也发布了here。 如果你能递归这个就好了..即访问myobj.subdict.subdict @Pithikos 有一个第 3 方库提供该功能,请查看 python-box。【参考方案2】:

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

它确实看起来不那么冗长和整洁;但从长远来看,它只是晦涩难懂。字典就是字典,试图让它们表现得像具有属性的对象可能会导致(坏的)意外。

如果您需要像操作字典一样操作对象的字段,您可以随时在需要时使用内部的__dict__ 属性,然后明确清晰你在做什么。或者使用getattr(obj, 'key')也考虑到继承结构和类属性。

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

【讨论】:

一个意外的例子:如果你在字典中有一个键,它恰好是一个 python 关键字,例如字符串'for',然后属性访问失败,并且没有优雅的方法来正确处理这种情况。整个想法从一开始就被彻底打破了。 另一方面,自动完成使代码更高效、更不容易出错。这将完美地工作如果可以预定义字典结构。 Javascript 很优雅。 @QianChen JavaScript 可以优雅,也可以一团糟(就像大多数语言一样)。肯定会一团糟的是试图像使用 JavaScript(或任何其他两种语言)一样使用 Python。如果有一种预期的方式可以用给定的语言做某事,那么偏离它只会导致痛苦。【参考方案3】:

除了this answer,还可以添加对嵌套字典的支持:

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

请注意,这不支持内部某处的 dicts 的点表示法,例如列表。

【讨论】:

【参考方案4】:

您可以使用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)

【讨论】:

这是一种通过点分符号访问数据结构的有趣方式,但它似乎与 JSON 或 dict 并不特别兼容。有些库在提供 JSON 和 dict 支持的情况下使用命名元组。试试github.com/dsc/bunch 或github.com/kennknowles/python-jsonpath-rw 对于 python >= 3.6,考虑from typing import NamedTuple【参考方案5】:

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

附带说明:不要使用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,并且您不需要支持旧版本),您可以只使用命名元组:http://docs.python.org/2/library/collections.html#collections.namedtuple

【讨论】:

【参考方案6】:

使用__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>

【讨论】:

__getattr__ 仅在没有匹配其他查找规则时用作备用,因此无需查找self.__dict__[key],如果调用__getattr__,则已完成。如果 self._response['key'] 上的查找失败,只需提高 AttributeError 就足够了。 @brunodesthuilliers 看起来你是对的。奇怪的。也许这只是在 Python v1.5 时代才有必要。【参考方案7】:

可以使用内置方法argparse.Namespace()

import argparse

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

print(args.name)
# 'value'

您也可以通过vars(args)获取原始字典。

【讨论】:

【参考方案8】:

我使用dotted_dict 包:

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

【讨论】:

【参考方案9】:

这是一个简单、方便的点符号帮助器示例,用于处理嵌套项:

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

使用示例:

my_dict = "test1": "str1", "nested_dict": "test2": "str2", "nested_list": ["str3", "test4": "str4"]
print(data_dict_get(my_dict, "test1"))
# str1
print(data_dict_get(my_dict, "nested_dict.test2"))
# str2
print(data_dict_get(my_dict, "nested_list.1.test4"))
# str4

【讨论】:

经过大量搜索,这是我找到的最佳答案。在使用示例下,您应该将函数名称重命名为 dict_get 而不是 data_dict_get ,谢谢【参考方案10】:

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

print text

在此处了解更多信息:https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html

【讨论】:

虽然这会在技术上解决问题中提到的特定情况,但破解内置行为以修补其他内置行为的破解在客观上是糟糕的代码。【参考方案11】:

在this answer 的基础上添加一点点,您也可以支持列表:

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)

【讨论】:

不幸的是,虽然您的编码可能是正确的,但它与问题没有直接关系。问题是为什么打印对象本身会返回错误。

以上是关于如何在python中为dict使用点符号?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中为类型定义别名以进行类型提示

如何在 python 2 中为元类替换 __prepare__

Python - 如何在 django 模板中为所有值并排显示两个键的字典值

在 Python 中为中缀表示法添加前缀表示法

dict url的dict在Python中为post请求编码了params?

java - 如何在java printf中为数字符号保留符号?