用numpy支持覆盖一个字典

时间:2016-04-16 12:01:18

标签: python numpy inheritance dictionary

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

对此可能出错的任何建议?这甚至是正确的方法吗?

2 个答案:

答案 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)的解决方案,但这不起作用(有关详细信息,请参阅我的编辑历史记录)。但我发现将Extendeddictdict混合起来确实有效:

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检查每个元素是否可迭代(通过使用leniter),因为传入的内容可能被解释为多维数组。

这里有一个问题: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.Mappingcollections.MutableMapping不方便,则相当聪明和方便。我认为这是一个 Bug