我是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'
为什么会发生这种情况?
这个功能已经存在于标准库中,所以我建议您使用它们的类。
>>> 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'}
字典中的键应该是字符串标识符,以便属性访问能够正常工作。
Simple命名空间在Python 3.3中添加。对于该语言的旧版本,argparse.Namespace
具有类似的行为。
我假设你对Javascript很熟悉,想借用这种语法…我可以以个人经验告诉你,这不是一个好主意。
它确实看起来不那么啰嗦和整洁;但从长远来看,它只是模糊的。字典就是字典,试图让它们表现得像带有属性的对象可能会导致(糟糕的)意外。
如果您需要像操作字典一样操作对象的字段,那么您总是可以在需要时使用内部__dict__
属性,然后显式地清除您正在做的事情。或者使用getattr(obj, 'key')
来考虑继承结构和类属性。
但是通过阅读你的例子,你似乎在尝试不同的东西…因为点运算符将在没有任何额外代码的情况下查找__dict__
属性。
除了这个答案,还可以添加对嵌套字典的支持:
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
请注意,这不支持点表示法的字典在某个地方,例如列表。
可以使用命名元组吗?
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)
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)
__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,你不需要支持旧版本),你可以只使用namedtuple: http://docs.python.org/2/library/collections.html#collections.namedtuple
你可以使用内置方法argparse.Namespace()
:
import argparse
args = argparse.Namespace()
args.name = 'value'
print(args.name)
# 'value'
您也可以通过vars(args)
获取原始字典
使用__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>
这是一个简单,方便的点符号助手示例,用于嵌套项:
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
除了这个答案,你还可以支持列表:
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)
2022答案:我已经创建了dotwiz
包——这是一个快速的小库,在大多数情况下似乎表现得很好。
>>> from dotwiz import DotWiz
>>> test = DotWiz(hello='world')
>>> test.works = True
>>> test
✫(hello='world', works=True)
>>> test.hello
'world'
>>> assert test.works
此功能被纳入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)
安装: pip install omegaconf
这个库在配置时很方便,它实际上是为配置而设计的:
from omegaconf import OmegaConf
cfg = OmegaConf.load('config.yml')
print(cfg.data_path)
我使用dotted_dict
包:
>>> from dotted_dict import DottedDict
>>> test = DottedDict()
>>> test.name = 'value'
>>> print(test.name)
value
优于SimpleNamespace
的优点
(见@win的回答)DottedDict
是实际的dict
:
>>> isinstance(test, dict)
True
这允许,例如,检查成员:
>>> 'name' in test
True
而对于SimpleNamespace
,你需要像hasattr(test, 'name')
那样可读性差得多的东西。
不要使用DotMap
我发现这个困难的方式。如果引用非成员,它会添加它而不是抛出错误。这会导致很难在代码中找到bug:
>>> from dotmap import DotMap
>>> dm = DotMap()
>>> 'a' in dm
False
>>> x = dm.a
>>> 'a' in dm
True
#!/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}))
您可以为Dicts添加点符号,但总是存在命名空间问题。比如,这是做什么的?
x = DotDict()
x["values"] = 1989
print(x. values)
我使用pydash,它是JS的lodash的Python端口,当嵌套太难看时,以不同的方式完成这些事情。
为该类添加一个__repr__()
方法,以便您可以自定义显示在
print text
了解更多信息:https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html