用numpy supp重写dict

2024-04-24 21:09:18 发布

您现在位置:Python中文网/ 问答频道 /正文

使用How to "perfectly" override a dict?中的基本思想,我基于字典编写了一个类,该字典应该支持指定点分隔键,即Extendeddict('level1.level2', 'value') == {'level1':{'level2':'value'}}

代码是

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]

但是在Python2.7.10和Numpy1.11.0中

^{pr2}$

我得到:

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))产生{}

你有什么建议吗?这样做对吗?在


Tags: storekeyintestselfnumpyfielddictionary
2条回答

问题出在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 

但如果我做一个数组,并分配对象,它可以很好地工作

^{pr2}$

np.array是一个与dtype=object一起使用的复杂函数。比较np.array([[1,2],[2,3]],dtype=object)和{}。一个是(2,2),另一个是(2,)。它尝试生成一个2d数组,并且只有在失败时才使用列表元素的1d。沿着这条线发生了一些事情。在

我看到了两个解决方案-一个是关于构造数组的方法,我在其他场合也用过。另一个是要弄清楚为什么np.array没有深入dict而是你的。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(...)不同(就像混合列表一样)。在

纽比试着做它应该做的事情:

Numpy会检查每个元素是否是iterable(通过使用leniter),因为传入的内容可能被解释为多维数组。在

这里有一个问题:dict-类类(意思是isinstance(element, dict) == True)不会被解释为另一个维度(这就是为什么传入[{}]起作用的原因)。也许他们应该检查它是不是一个collections.Mapping而不是dict。也许你可以在他们的issue tracker上写一个bug。在

如果将类定义更改为:

class Extendeddict(collections.MutableMapping, dict):
     ...

更改__len__-方法:

^{pr2}$

它起作用了。这两种方法都不是你想做的,但是numpy只是使用duck-typing来创建数组,并且没有直接从dict派生子类,或者通过使len不可访问,numpy将你的类看作应该是另一个维度的东西。如果您想传入自定义序列(来自collections.Sequence的子类),但对于collections.Mapping或{},这是相当聪明和方便的。我认为这是一个错误。在

相关问题 更多 >