121

我对 python 很陌生,我希望我可以.用符号来访问 a 的值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'

为什么会这样?

4

11 回答 11

209

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

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

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

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

于 2013-04-29T13:15:06.610 回答
53

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

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

如果您需要像操作字典一样操作对象的字段,您总是可以__dict__在需要时使用 internal 属性,然后就可以明确地知道您在做什么。或者使用getattr(obj, 'key')也要考虑继承结构和类属性。

但是通过阅读您的示例,您似乎正在尝试不同的东西......因为点运算符已经在__dict__没有任何额外代码的情况下查看属性。

于 2013-04-29T13:07:59.190 回答
12

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

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

请注意,这不支持列表中某处的字典的点表示法。

于 2019-01-23T17:36:43.353 回答
9

你可以使用命名元组吗?

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)

于 2013-04-29T13:09:42.340 回答
6

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

于 2013-04-29T14:22:48.677 回答
3

使用时必须小心__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>
于 2013-04-29T13:15:09.320 回答
3

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

import argparse

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

print(args.name)
# 'value'

您还可以通过vars(args).

于 2021-01-26T01:39:19.580 回答
3

这是一个使用嵌套项的简单、方便的点表示法帮助器示例:

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(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
于 2021-07-06T21:12:05.283 回答
1

我使用这个dotted_dict包:

>>> from dotted_dict import DottedDict
>>> test = DottedDict()
>>> test.name = 'value'
>>> print(test.name)
value
于 2021-05-13T19:31:19.767 回答
1

这个答案做一点补充,你也可以支持列表:

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)
于 2021-05-05T10:18:40.127 回答
0

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

print text

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

于 2013-04-29T13:00:46.920 回答