[英]How to use a dot "." to access members of dictionary?
如何使 Python 字典成員可以通過點“.”訪問?
例如,我不想寫mydict['val']
,而是寫mydict.val
。
我也想以這種方式訪問嵌套的字典。 例如
mydict.mydict2.val
會指
mydict = { 'mydict2': { 'val': ... } }
我一直把它保存在一個 util 文件中。 你也可以在你自己的類中使用它作為一個 mixin。
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'
mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'
你可以使用我剛剛制作的這個類來做到這一點。 使用此類,您可以像使用另一個字典(包括 json 序列化)或使用點符號一樣使用Map
對象。 我希望能幫助你:
class Map(dict):
"""
Example:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
"""
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self[k] = v
if kwargs:
for k, v in kwargs.iteritems():
self[k] = v
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
使用示例:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']
通過dotmap
安裝pip
圖
pip install dotmap
它做你想做的所有事情,並繼承dict
,所以它像普通字典一樣運行:
from dotmap import DotMap
m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'
最重要的是,您可以將其與dict
對象相互轉換:
d = m.toDict()
m = DotMap(d) # automatic conversion in constructor
這意味着如果您要訪問的內容已經是dict
形式,您可以將其轉換為DotMap
以便於訪問:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city
最后,它會自動創建新的子DotMap
實例,因此您可以執行以下操作:
m = DotMap()
m.people.steve.age = 31
全面披露:我是DotMap的創建者。 我創建它是因為Bunch
缺少這些功能
DotMap
,當您有很多層次結構時,這可以節省時間並讓代碼更簡潔dict
構造並遞歸地將所有子dict
實例轉換為DotMap
使用SimpleNamespace
:
>>> from types import SimpleNamespace
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])
Fabric有一個非常好的、最小的實現。 擴展它以允許嵌套訪問,我們可以使用defaultdict
,結果如下所示:
from collections import defaultdict
class AttributeDict(defaultdict):
def __init__(self):
super(AttributeDict, self).__init__(AttributeDict)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
self[key] = value
如下使用它:
keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234
這詳細說明了 Kugel 對“從 dict 派生並實現__getattr__
和__setattr__
”的回答。 現在你知道怎么做了!
我試過這個:
class dotdict(dict):
def __getattr__(self, name):
return self[name]
你也可以試試__getattribute__
。
使每個 dict 成為一種 dotdict 就足夠了,如果您想從多層 dict 初始化它,請嘗試實現__init__
。
如果要腌制修改后的字典,則需要在上述答案中添加一些狀態方法:
class DotDict(dict):
"""dot.notation access to dictionary attributes"""
def __getattr__(self, attr):
return self.get(attr)
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __getstate__(self):
return self
def __setstate__(self, state):
self.update(state)
self.__dict__ = self
不。 屬性訪問和索引在 Python 中是不同的事情,您不應該希望它們執行相同的操作。 如果您有一些應該具有可訪問屬性並使用[]
表示法從字典中獲取項目的東西,請創建一個類(可能由namedtuple
創建一個)。
基於 Kugel 的回答並考慮到 Mike Graham 的謹慎言論,如果我們制作一個包裝器會怎樣?
class DictWrap(object):
""" Wrap an existing dict, or create a new one, and access with either dot
notation or key lookup.
The attribute _data is reserved and stores the underlying dictionary.
When using the += operator with create=True, the empty nested dict is
replaced with the operand, effectively creating a default dictionary
of mixed types.
args:
d({}): Existing dict to wrap, an empty dict is created by default
create(True): Create an empty, nested dict instead of raising a KeyError
example:
>>>dw = DictWrap({'pp':3})
>>>dw.a.b += 2
>>>dw.a.b += 2
>>>dw.a['c'] += 'Hello'
>>>dw.a['c'] += ' World'
>>>dw.a.d
>>>print dw._data
{'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}
"""
def __init__(self, d=None, create=True):
if d is None:
d = {}
supr = super(DictWrap, self)
supr.__setattr__('_data', d)
supr.__setattr__('__create', create)
def __getattr__(self, name):
try:
value = self._data[name]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[name] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setattr__(self, name, value):
self._data[name] = value
def __getitem__(self, key):
try:
value = self._data[key]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[key] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setitem__(self, key, value):
self._data[key] = value
def __iadd__(self, other):
if self._data:
raise TypeError("A Nested dict will only be replaced if it's empty")
else:
return other
為了建立 epool 的答案,此版本允許您通過點運算符訪問內部的任何 dict:
foo = {
"bar" : {
"baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
}
}
例如, foo.bar.baz[1].baba
返回"loo"
。
class Map(dict):
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.items():
if isinstance(v, dict):
v = Map(v)
if isinstance(v, list):
self.__convert(v)
self[k] = v
if kwargs:
for k, v in kwargs.items():
if isinstance(v, dict):
v = Map(v)
elif isinstance(v, list):
self.__convert(v)
self[k] = v
def __convert(self, v):
for elem in range(0, len(v)):
if isinstance(v[elem], dict):
v[elem] = Map(v[elem])
elif isinstance(v[elem], list):
self.__convert(v[elem])
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
您可以使用 SimpleNamespace 實現此目的
from types import SimpleNamespace
# Assign values
args = SimpleNamespace()
args.username = 'admin'
# Retrive values
print(args.username) # output: admin
使用__getattr__
,非常簡單,適用於 Python 3.4.3
class myDict(dict):
def __getattr__(self,val):
return self[val]
blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)
輸出:
10000
StackOverflow
我喜歡Munch ,它在點訪問之上提供了許多方便的選項。
進口蒙克
temp_1 = {'person':{'fname':'senthil','lname':'ramalingam'}}
dict_munch = munch.munchify(temp_1)
dict_munch.person.fname
語言本身不支持這一點,但有時這仍然是一個有用的要求。 除了 Bunch 配方之外,您還可以編寫一個小方法,該方法可以使用點分字符串訪問字典:
def get_var(input_dict, accessor_string):
"""Gets data from a dictionary using a dotted accessor-string"""
current_data = input_dict
for chunk in accessor_string.split('.'):
current_data = current_data.get(chunk, {})
return current_data
這將支持這樣的事情:
>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>
我最終嘗試了AttrDict和Bunch庫,發現它們會降低我的使用速度。 在我和一個朋友調查之后,我們發現編寫這些庫的主要方法導致庫通過嵌套對象積極遞歸並在整個過程中復制字典對象。 考慮到這一點,我們進行了兩項關鍵更改。 1)我們使屬性延遲加載 2)我們不是創建字典對象的副本,而是創建輕量級代理對象的副本。 這是最終的實現。 使用此代碼的性能提升令人難以置信。 當使用 AttrDict 或 Bunch 時,這兩個庫分別消耗了我請求時間的 1/2 和 1/3(什么!?)。 這段代碼將該時間減少到幾乎沒有(在 0.5 毫秒的范圍內)。 這當然取決於您的需求,但如果您在代碼中大量使用此功能,那么一定要使用像這樣簡單的東西。
class DictProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
def __getattr__(self, key):
try:
return wrap(getattr(self.obj, key))
except AttributeError:
try:
return self[key]
except KeyError:
raise AttributeError(key)
# you probably also want to proxy important list properties along like
# items(), iteritems() and __len__
class ListProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
# you probably also want to proxy important list properties along like
# __iter__ and __len__
def wrap(value):
if isinstance(value, dict):
return DictProxy(value)
if isinstance(value, (tuple, list)):
return ListProxy(value)
return value
請參閱https://stackoverflow.com/users/704327/michael-merickel此處的原始實現。
需要注意的另一件事是,此實現非常簡單,並沒有實現您可能需要的所有方法。 您需要根據需要在 DictProxy 或 ListProxy 對象上編寫這些內容。
該解決方案是對epool提供的解決方案的改進,以解決 OP 以一致方式訪問嵌套字典的要求。 epool 的解決方案不允許訪問嵌套的字典。
class YAMLobj(dict):
def __init__(self, args):
super(YAMLobj, self).__init__(args)
if isinstance(args, dict):
for k, v in args.iteritems():
if not isinstance(v, dict):
self[k] = v
else:
self.__setattr__(k, YAMLobj(v))
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(YAMLobj, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(YAMLobj, self).__delitem__(key)
del self.__dict__[key]
有了這個類,現在可以做類似的事情: ABCD
。
def dict_to_object(dick):
# http://stackoverflow.com/a/1305663/968442
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
return Struct(**dick)
如果一個人決定將該dict
永久轉換為對象,則應該這樣做。 您可以在訪問之前創建一次性對象。
d = dict_to_object(d)
對於字典、列表、字典列表和列表字典的無限嵌套級別。
它還支持酸洗
這是這個答案的延伸。
class DotDict(dict):
# https://stackoverflow.com/a/70665030/913098
"""
Example:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
Iterable are assumed to have a constructor taking list as input.
"""
def __init__(self, *args, **kwargs):
super(DotDict, self).__init__(*args, **kwargs)
args_with_kwargs = []
for arg in args:
args_with_kwargs.append(arg)
args_with_kwargs.append(kwargs)
args = args_with_kwargs
for arg in args:
if isinstance(arg, dict):
for k, v in arg.items():
self[k] = v
if isinstance(v, dict):
self[k] = DotDict(v)
elif isinstance(v, str) or isinstance(v, bytes):
self[k] = v
elif isinstance(v, Iterable):
klass = type(v)
map_value: List[Any] = []
for e in v:
map_e = DotDict(e) if isinstance(e, dict) else e
map_value.append(map_e)
self[k] = klass(map_value)
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(DotDict, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(DotDict, self).__delitem__(key)
del self.__dict__[key]
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
self.__dict__.update(d)
if __name__ == "__main__":
import pickle
def test_map():
d = {
"a": 1,
"b": {
"c": "d",
"e": 2,
"f": None
},
"g": [],
"h": [1, "i"],
"j": [1, "k", {}],
"l":
[
1,
"m",
{
"n": [3],
"o": "p",
"q": {
"r": "s",
"t": ["u", 5, {"v": "w"}, ],
"x": ("z", 1)
}
}
],
}
map_d = DotDict(d)
w = map_d.l[2].q.t[2].v
assert w == "w"
pickled = pickle.dumps(map_d)
unpickled = pickle.loads(pickled)
assert unpickled == map_d
kwargs_check = DotDict(a=1, b=[dict(c=2, d="3"), 5])
assert kwargs_check.b[0].d == "3"
kwargs_and_args_check = DotDict(d, a=1, b=[dict(c=2, d="3"), 5])
assert kwargs_and_args_check.l[2].q.t[2].v == "w"
assert kwargs_and_args_check.b[0].d == "3"
test_map()
這也適用於嵌套的字典,並確保稍后附加的字典表現相同:
class DotDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Recursively turn nested dicts into DotDicts
for key, value in self.items():
if type(value) is dict:
self[key] = DotDict(value)
def __setitem__(self, key, item):
if type(item) is dict:
item = DotDict(item)
super().__setitem__(key, item)
__setattr__ = __setitem__
__getattr__ = dict.__getitem__
使用namedtuple
允許點訪問。
它就像一個輕量級對象,也具有元組的屬性。
它允許定義屬性並使用點運算符訪問它們。
from collections import namedtuple
Data = namedtuple('Data', ['key1', 'key2'])
dataObj = Data(val1, key2=val2) # can instantiate using keyword arguments and positional arguments
使用點運算符訪問
dataObj.key1 # Gives val1
datObj.key2 # Gives val2
使用元組索引訪問
dataObj[0] # Gives val1
dataObj[1] # Gives val2
但請記住這是一個元組; 不是 dict 。 所以下面的代碼會報錯
dataObj['key1'] # Gives TypeError: tuple indices must be integers or slices, not str
參考: namedtuple
這是一個老問題,但我最近發現sklearn
有一個可通過密鑰訪問的實現版本dict
,即Bunch
https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html#sklearn.utils。束
最簡單的解決方案。
定義一個只有 pass 語句的類。 為此類創建對象並使用點表示法。
class my_dict:
pass
person = my_dict()
person.id = 1 # create using dot notation
person.phone = 9999
del person.phone # Remove a property using dot notation
name_data = my_dict()
name_data.first_name = 'Arnold'
name_data.last_name = 'Schwarzenegger'
person.name = name_data
person.name.first_name # dot notation access for nested properties - gives Arnold
獲得點訪問(但不是數組訪問)的一種簡單方法是在 Python 中使用普通對象。 像這樣:
class YourObject:
def __init__(self, *args, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
...並像這樣使用它:
>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"
...將其轉換為字典:
>>> print(obj.__dict__)
{"key": "value"}
@derek73 的答案非常簡潔,但它不能被腌制或(深度)復制,並且它返回None
丟失鍵。 下面的代碼解決了這個問題。
編輯:我沒有看到上面的答案解決了完全相同的問題(贊成)。 我將答案留在這里以供參考。
class dotdict(dict):
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
我只需要使用點路徑字符串訪問字典,所以我想出了:
def get_value_from_path(dictionary, parts):
""" extracts a value from a dictionary using a dotted path string """
if type(parts) is str:
parts = parts.split('.')
if len(parts) > 1:
return get_value_from_path(dictionary[parts[0]], parts[1:])
return dictionary[parts[0]]
a = {'a':{'b':'c'}}
print(get_value_from_path(a, 'a.b')) # c
我想把我自己的解決方案扔進戒指:
https://github.com/skorokithakis/jsane
它允許您將 JSON 解析為您可以使用with.attribute.lookups.like.this.r()
訪問的with.attribute.lookups.like.this.r()
,主要是因為我在開始研究之前沒有看到這個答案。
不是對 OP 問題的直接回答,而是受到某些人的啟發,也許對某些人有用。我使用內部__dict__
創建了一個基於對象的解決方案(絕不是優化代碼)
payload = {
"name": "John",
"location": {
"lat": 53.12312312,
"long": 43.21345112
},
"numbers": [
{
"role": "home",
"number": "070-12345678"
},
{
"role": "office",
"number": "070-12345679"
}
]
}
class Map(object):
"""
Dot style access to object members, access raw values
with an underscore e.g.
class Foo(Map):
def foo(self):
return self.get('foo') + 'bar'
obj = Foo(**{'foo': 'foo'})
obj.foo => 'foobar'
obj._foo => 'foo'
"""
def __init__(self, *args, **kwargs):
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self.__dict__[k] = v
self.__dict__['_' + k] = v
if kwargs:
for k, v in kwargs.iteritems():
self.__dict__[k] = v
self.__dict__['_' + k] = v
def __getattribute__(self, attr):
if hasattr(self, 'get_' + attr):
return object.__getattribute__(self, 'get_' + attr)()
else:
return object.__getattribute__(self, attr)
def get(self, key):
try:
return self.__dict__.get('get_' + key)()
except (AttributeError, TypeError):
return self.__dict__.get(key)
def __repr__(self):
return u"<{name} object>".format(
name=self.__class__.__name__
)
class Number(Map):
def get_role(self):
return self.get('role')
def get_number(self):
return self.get('number')
class Location(Map):
def get_latitude(self):
return self.get('lat') + 1
def get_longitude(self):
return self.get('long') + 1
class Item(Map):
def get_name(self):
return self.get('name') + " Doe"
def get_location(self):
return Location(**self.get('location'))
def get_numbers(self):
return [Number(**n) for n in self.get('numbers')]
# Tests
obj = Item({'foo': 'bar'}, **payload)
assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112
for n in obj.numbers:
assert type(n) == Number
if n.role == 'home':
assert n.number == "070-12345678"
if n.role == 'office':
assert n.number == "070-12345679"
這是我的@derek73 answer版本。 我使用dict.__getitem__
作為__getattr__
所以它仍然拋出KeyError
,並且我用 " " 前綴重命名 dict 公共方法(用 " " 包圍會導致特殊方法名稱沖突,如__get__
將被視為描述符方法)。 無論如何,由於關鍵的dict
基本方法,您無法將鍵的名稱空間作為屬性完全清楚,因此解決方案並不完美,但您可以擁有鍵 - 屬性,如get
、 pop
、 items
等。
class DotDictMeta(type):
def __new__(
cls,
name,
bases,
attrs,
rename_method=lambda n: f'__{n}__',
**custom_methods,
):
d = dict
attrs.update(
cls.get_hidden_or_renamed_methods(rename_method),
__getattr__=d.__getitem__,
__setattr__=d.__setitem__,
__delattr__=d.__delitem__,
**custom_methods,
)
return super().__new__(cls, name, bases, attrs)
def __init__(self, name, bases, attrs, **_):
super().__init__(name, bases, attrs)
@property
def attribute_error(self):
raise AttributeError
@classmethod
def get_hidden_or_renamed_methods(cls, rename_method=None):
public_methods = tuple(
i for i in dict.__dict__.items() if not i[0].startswith('__')
)
error = cls.attribute_error
hidden_methods = ((k, error) for k, v in public_methods)
yield from hidden_methods
if rename_method:
renamed_methods = ((rename_method(k), v) for k, v in public_methods)
yield from renamed_methods
class DotDict(dict, metaclass=DotDictMeta):
pass
您可以從 DotDict 命名空間中刪除 dict 方法並繼續使用 dict 類方法,當您想要對其他 dict 實例進行操作並且想要使用相同的方法而不額外檢查其是否 DotDict 時,它也很有用,例如。
dct = dict(a=1)
dot_dct = DotDict(b=2)
foo = {c: i for i, c in enumerate('xyz')}
for d in (dct, dot_dct):
# you would have to use dct.update and dot_dct.__update methods
dict.update(d, foo)
assert dict.get(dot, 'foo', 0) is 0
我只是從很久以前從事的一個項目中挖掘出來的。 它可能會被優化一點,但它就在這里。
class DotNotation(dict):
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __init__(self, data):
if isinstance(data, str):
data = json.loads(data)
for name, value in data.items():
setattr(self, name, self._wrap(value))
def __getattr__(self, attr):
def _traverse(obj, attr):
if self._is_indexable(obj):
try:
return obj[int(attr)]
except:
return None
elif isinstance(obj, dict):
return obj.get(attr, None)
else:
return attr
if '.' in attr:
return reduce(_traverse, attr.split('.'), self)
return self.get(attr, None)
def _wrap(self, value):
if self._is_indexable(value):
# (!) recursive (!)
return type(value)([self._wrap(v) for v in value])
elif isinstance(value, dict):
return DotNotation(value)
else:
return value
@staticmethod
def _is_indexable(obj):
return isinstance(obj, (tuple, list, set, frozenset))
if __name__ == "__main__":
test_dict = {
"dimensions": {
"length": "112",
"width": "103",
"height": "42"
},
"meta_data": [
{
"id": 11089769,
"key": "imported_gallery_files",
"value": [
"https://example.com/wp-content/uploads/2019/09/unnamed-3.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-2.jpg",
"https://example.com/wp-content/uploads/2019/09/unnamed-4.jpg"
]
}
]
}
dotted_dict = DotNotation(test_dict)
print(dotted_dict.dimensions.length) # => '112'
print(getattr(dotted_dict, 'dimensions.length')) # => '112'
print(dotted_dict.meta_data[0].key) # => 'imported_gallery_files'
print(getattr(dotted_dict, 'meta_data.0.key')) # => 'imported_gallery_files'
print(dotted_dict.meta_data[0].value) # => ['link1','link2','link2']
print(getattr(dotted_dict, 'meta_data.0.value')) # => ['link1','link2','link3']
print(dotted_dict.meta_data[0].value[2]) # => 'link3'
print(getattr(dotted_dict, 'meta_data.0.value.2')) # => 'link3'
我的 2 美分:出於我自己的目的,我開發了minydra
,一個簡單的命令行解析器,其中包括一個自定義類MinyDict
(受addict
啟發):
In [1]: from minydra import MinyDict
In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
╭──────────────────────────────╮
│ foo : bar │
│ yes.no.maybe : idontknow │
╰──────────────────────────────╯
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}
In [3]: args.resolve().pretty_print(); args
╭──────────────────────────╮
│ foo : bar │
│ yes │
│ │no │
│ │ │maybe : idontknow │
╰──────────────────────────╯
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}
In [4]: args.yes.no.maybe
Out[4]: "idontknow"
In [5]: "foo" in args
Out[5]: True
In [6]: "rick" in args
Out[6]: False
In [7]: args.morty is None
Out[7]: True
In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])
它通過向/從json
yaml
和pickle
添加轉儲/加載方法比addict
更進一步,並且在MinyDict.update()
中還有一個strict
模式以防止創建新密鑰(這對於防止命令行中的拼寫錯誤很有用)
我不喜歡將另一個日志添加到(超過)10 年的火災中,但我也會查看我最近發布的dotwiz
庫 - 實際上就在今年。
這是一個相對較小的庫,至少與其他替代方案相比,它在基准測試中的獲取(訪問)和設置(創建)時間方面也表現得非常好。
通過pip
安裝dotwiz
pip install dotwiz
它做你想做的所有事情,並繼承dict
,所以它像普通字典一樣運行:
from dotwiz import DotWiz
dw = DotWiz()
dw.hello = 'world'
dw.hello
dw.hello += '!'
# dw.hello and dw['hello'] now both return 'world!'
dw.val = 5
dw.val2 = 'Sam'
最重要的是,您可以將其與dict
對象相互轉換:
d = dw.to_dict()
dw = DotWiz(d) # automatic conversion in constructor
這意味着如果您要訪問的內容已經是dict
形式,您可以將其轉換為DotWiz
以便於訪問:
import json
json_dict = json.loads(text)
data = DotWiz(json_dict)
print data.location.city
最后,我正在處理的令人興奮的事情是現有功能請求,以便它自動創建新的子DotWiz
實例,以便您可以執行以下操作:
dw = DotWiz()
dw['people.steve.age'] = 31
dw
# ✫(people=✫(steve=✫(age=31)))
dotmap
我在下面添加了與dotmap
的快速而骯臟的性能比較。
首先,使用pip
安裝兩個庫:
pip install dotwiz dotmap
我想出了以下代碼用於基准測試:
from timeit import timeit
from dotwiz import DotWiz
from dotmap import DotMap
d = {'hey': {'so': [{'this': {'is': {'pretty': {'cool': True}}}}]}}
dw = DotWiz(d)
# ✫(hey=✫(so=[✫(this=✫(is=✫(pretty={'cool'})))]))
dm = DotMap(d)
# DotMap(hey=DotMap(so=[DotMap(this=DotMap(is=DotMap(pretty={'cool'})))]))
assert dw.hey.so[0].this['is'].pretty.cool == dm.hey.so[0].this['is'].pretty.cool
n = 100_000
print('dotwiz (create): ', round(timeit('DotWiz(d)', number=n, globals=globals()), 3))
print('dotmap (create): ', round(timeit('DotMap(d)', number=n, globals=globals()), 3))
print('dotwiz (get): ', round(timeit("dw.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
print('dotmap (get): ', round(timeit("dm.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
結果,在我的 M1 Mac 上,運行 Python 3.10:
dotwiz (create): 0.189
dotmap (create): 1.085
dotwiz (get): 0.014
dotmap (get): 0.335
kaggle_environments使用的實現是一個名為structify
的函數。
class Struct(dict):
def __init__(self, **entries):
entries = {k: v for k, v in entries.items() if k != "items"}
dict.__init__(self, entries)
self.__dict__.update(entries)
def __setattr__(self, attr, value):
self.__dict__[attr] = value
self[attr] = value
# Added benefit of cloning lists and dicts.
def structify(o):
if isinstance(o, list):
return [structify(o[i]) for i in range(len(o))]
elif isinstance(o, dict):
return Struct(**{k: structify(v) for k, v in o.items()})
return o
這對於在ConnectX等游戲中測試 AI 模擬代理可能很有用
from kaggle_environments import structify
obs = structify({ 'remainingOverageTime': 60, 'step': 0, 'mark': 1, 'board': [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
conf = structify({ 'timeout': 2, 'actTimeout': 2, 'agentTimeout': 60, 'episodeSteps': 1000, 'runTimeout': 1200, 'columns': 7, 'rows': 6, 'inarow': 4, '__raw_path__': '/kaggle_simulations/agent/main.py' })
def agent(obs, conf):
action = obs.step % conf.columns
return action
如果您已經在使用 pandas,您可以構建一個 pandas 系列或 DataFrame 可以通過點語法訪問項目:
import pandas as pd
my_dictionary = pd.Series({
'key1': 'value1',
'key2': 'value2'
})
print(my_dictionary.key1)
# Output: value1
import pandas as pd
my_dictionary = pd.DataFrame({
'key1': {
'inner_key1': 'value1'
},
'key2': {
'inner_key2': 'value2'
}
})
print(my_dictionary.key1.inner_key1)
# Output: value1
請注意,這可能更適用於規范化的數據結構(其中每個字典條目具有相同的結構)。 在上面的第二個示例中,生成的 DataFrame 是:
key1 key2
inner_key1 value1 NaN
inner_key2 NaN value2
可以使用dotsi來獲得完整列表、字典和遞歸支持,以及一些擴展方法
pip install dotsi
和
>>> import dotsi
>>>
>>> d = dotsi.Dict({"foo": {"bar": "baz"}}) # Basic
>>> d.foo.bar
'baz'
>>> d.users = [{"id": 0, "name": "Alice"}] # List
>>> d.users[0].name
'Alice'
>>> d.users.append({"id": 1, "name": "Becca"}); # Append
>>> d.users[1].name
'Becca'
>>> d.users += [{"id": 2, "name": "Cathy"}]; # `+=`
>>> d.users[2].name
'Cathy'
>>> d.update({"tasks": [{"id": "a", "text": "Task A"}]});
>>> d.tasks[0].text
'Task A'
>>> d.tasks[0].tags = ["red", "white", "blue"];
>>> d.tasks[0].tags[2];
'blue'
>>> d.tasks[0].pop("tags") # `.pop()`
['red', 'white', 'blue']
>>>
>>> import pprint
>>> pprint.pprint(d)
{'foo': {'bar': 'baz'},
'tasks': [{'id': 'a', 'text': 'Task A'}],
'users': [{'id': 0, 'name': 'Alice'},
{'id': 1, 'name': 'Becca'},
{'id': 2, 'name': 'Cathy'}]}
>>>
>>> type(d.users) # dotsi.Dict (AKA dotsi.DotsiDict)
<class 'dotsi.DotsiList'>
>>> type(d.users[0]) # dotsi.List (AKA dotsi.DotsiList)
<class 'dotsi.DotsiDict'>
>>>
一種微妙的解決方案
class DotDict(dict):
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def __getattr__(self, key):
def typer(candidate):
if isinstance(candidate, dict):
return DotDict(candidate)
if isinstance(candidate, str): # iterable but no need to iter
return candidate
try: # other iterable are processed as list
return [typer(item) for item in candidate]
except TypeError:
return candidate
return candidate
return typer(dict.get(self, key))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.