使用How to "perfectly" override a dict?的基本思想,我根据应该支持分配点分隔键的词典编写了一个类,即<button id='nobtnAdd' disabled onclick='return saveClick();'
title="New Record (Alt+A)">Save
</button>
代码是
Extendeddict('level1.level2', 'value') == {'level1':{'level2':'value'}}
但是使用Python 2.7.10和numpy 1.11.0,运行
import collections
import numpy
class Extendeddict(collections.MutableMapping):
"""Dictionary overload class that adds functions to support chained keys, e.g. A.B.C
:rtype : Extendeddict
"""
# noinspection PyMissingConstructor
def __init__(self, *args, **kwargs):
self._store = dict()
self.update(dict(*args, **kwargs))
def __getitem__(self, key):
keys = self._keytransform(key)
print 'Original key: {0}\nTransformed keys: {1}'.format(key, keys)
if len(keys) == 1:
return self._store[key]
else:
key1 = '.'.join(keys[1:])
if keys[0] in self._store:
subdict = Extendeddict(self[keys[0]] or {})
try:
return subdict[key1]
except:
raise KeyError(key)
else:
raise KeyError(key)
def __setitem__(self, key, value):
keys = self._keytransform(key)
if len(keys) == 1:
self._store[key] = value
else:
key1 = '.'.join(keys[1:])
subdict = Extendeddict(self.get(keys[0]) or {})
subdict.update({key1: value})
self._store[keys[0]] = subdict._store
def __delitem__(self, key):
keys = self._keytransform(key)
if len(keys) == 1:
del self._store[key]
else:
key1 = '.'.join(keys[1:])
del self._store[keys[0]][key1]
if not self._store[keys[0]]:
del self._store[keys[0]]
def __iter__(self):
return iter(self._store)
def __len__(self):
return len(self._store)
def __repr__(self):
return self._store.__repr__()
# noinspection PyMethodMayBeStatic
def _keytransform(self, key):
try:
return key.split('.')
except:
return [key]
我明白了:
basic = {'Test.field': 'test'}
print 'Normal dictionary: {0}'.format(basic)
print 'Normal dictionary in a list: {0}'.format([basic])
print 'Normal dictionary in numpy array: {0}'.format(numpy.array([basic], dtype=object))
print 'Normal dictionary in numpy array.tolist(): {0}'.format(numpy.array([basic], dtype=object).tolist())
extended_dict = Extendeddict(basic)
print 'Extended dictionary: {0}'.format(extended_dict)
print 'Extended dictionary in a list: {0}'.format([extended_dict])
print 'Extended dictionary in numpy array: {0}'.format(numpy.array([extended_dict], dtype=object))
print 'Extended dictionary in numpy array.tolist(): {0}'.format(numpy.array([extended_dict], dtype=object).tolist())
我希望Normal dictionary: {'Test.field': 'test'}
Normal dictionary in a list: [{'Test.field': 'test'}]
Normal dictionary in numpy array: [{'Test.field': 'test'}]
Normal dictionary in numpy array.tolist(): [{'Test.field': 'test'}]
Original key: Test
Transformed keys: ['Test']
Extended dictionary: {'Test': {'field': 'test'}}
Extended dictionary in a list: [{'Test': {'field': 'test'}}]
Original key: 0
Transformed keys: [0]
Traceback (most recent call last):
File "/tmp/scratch_2.py", line 77, in <module>
print 'Extended dictionary in numpy array: {0}'.format(numpy.array([extended_dict], dtype=object))
File "/tmp/scratch_2.py", line 20, in __getitem__
return self._store[key]
KeyError: 0
能够导致print 'Extended dictionary in numpy array: {0}'.format(numpy.array([extended_dict], dtype=object))
对此可能出错的任何建议?这甚至是正确的方法吗?
答案 0 :(得分:3)
问题出在np.array
构造函数步骤中。它深入研究其输入,试图创建一个更高维的数组。
In [99]: basic={'test.field':'test'}
In [100]: eb=Extendeddict(basic)
In [104]: eba=np.array([eb],object)
<keys: 0,[0]>
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-104-5591a58c168a> in <module>()
----> 1 eba=np.array([eb],object)
<ipython-input-88-a7d937b1c8fd> in __getitem__(self, key)
11 keys = self._keytransform(key);print key;print keys
12 if len(keys) == 1:
---> 13 return self._store[key]
14 else:
15 key1 = '.'.join(keys[1:])
KeyError: 0
但是,如果我创建一个数组,并分配该对象,它可以正常工作
In [105]: eba=np.zeros((1,),object)
In [106]: eba[0]=eb
In [107]: eba
Out[107]: array([{'test': {'field': 'test'}}], dtype=object)
np.array
是与dtype=object
一起使用的棘手功能。比较np.array([[1,2],[2,3]],dtype=object)
和np.array([[1,2],[2]],dtype=object)
。一个是(2,2)另一个(2,)。它尝试创建一个二维数组,并且只有在失败时才使用列表元素转换为1d。这条路上发生了一些事情。
我看到了两个解决方案 - 一个是关于构建数组的方法,我在其他场合使用过。另一个是弄清楚为什么np.array
不会深入研究dict
,而是与你的一起。{1}}。编译np.array
,因此可能需要阅读严格的GITHUB代码。
我尝试使用f=np.frompyfunc(lambda x:x,1,1)
的解决方案,但这不起作用(有关详细信息,请参阅我的编辑历史记录)。但我发现将Extendeddict
和dict
混合起来确实有效:
In [139]: np.array([eb,basic])
Out[139]: array([{'test': {'field': 'test'}}, {'test.field': 'test'}], dtype=object)
将它与其他内容混合在一起,例如None
或空列表
In [140]: np.array([eb,[]])
Out[140]: array([{'test': {'field': 'test'}}, []], dtype=object)
In [142]: np.array([eb,None])[:-1]
Out[142]: array([{'test': {'field': 'test'}}], dtype=object)
这是构建列表对象数组的另一个常见技巧。
如果你给它两个或更多Extendeddict
不同长度
np.array([eb, Extendeddict({})])
。换句话说,如果len(...)
不同(就像混合列表一样)。
答案 1 :(得分:2)
Numpy试图做它应该做的事情:
Numpy检查每个元素是否可迭代(通过使用len
和iter
),因为传入的内容可能被解释为多维数组。
这里有一个问题:dict
- 类似的类(意为isinstance(element, dict) == True
)不会被解释为另一个维度(这就是传递[{}]
的原因)。可能他们应该检查它是collections.Mapping
而不是dict
。也许您可以在issue tracker上提交错误。
如果您将班级定义更改为:
class Extendeddict(collections.MutableMapping, dict):
...
或更改您的__len__
- 方法:
def __len__(self):
raise NotImplementedError
它有效。这些都不是你想要做的事情,但是numpy只是使用 duck typing 来创建数组而不直接从dict
继承子类,或者让len
无法访问numpy看到你的作为应该成为另一个维度的东西。如果您想传递自定义序列(来自collections.Sequence
的子类)但对collections.Mapping
或collections.MutableMapping
不方便,则相当聪明和方便。我认为这是一个 Bug 。