2016-04-16 9 views
3

Verwenden der Basisidee von How to "perfectly" override a dict?, codierte ich eine Klasse basierend auf Wörterbücher, den Punkt begrenzt Tasten zuweisen, dh Extendeddict('level1.level2', 'value') == {'level1':{'level2':'value'}}ein dict mit numpy Unterstützung

Der Code ist

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] 

Aber mit Python unterstützen sollte 2.7.10 und numpy 1.11.0,

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()) 

läuft ich:

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 

Während ich würde erwarten, print 'Extended dictionary in numpy array: {0}'.format(numpy.array([extended_dict], dtype=object)) in Extended dictionary in numpy array: [{'Test': {'field': 'test'}}]

Irgendwelche Vorschläge auf, führen, was für dieses falsch sein könnte? Ist das überhaupt der richtige Weg?

+2

es scheint mir, dass Sie versuchen, die [pandas] (http://pandas.pydata.org/pandas-docs/stable/10min.html) Bibliothek neu zu erfinden;) – MaxU

+0

@MaxU Pandas macht etwas ganz anderes von dem, was ich dafür brauchen würde, und ich benutze es für viele andere Dinge. Was ich will, ist ein "einfaches" Klassenwörterbuch wie das unterstützt s Punkt-getrennte Felder. –

+0

Fügen Sie einige Debugging-Ausdrucke hinzu, z. B. "Schlüssel" und "Schlüssel" in der Nähe des Fehlers. – hpaulj

Antwort

3

Das Problem liegt in der np.array Konstruktor Schritt. Es gräbt sich in seine Eingaben, um ein höherdimensionales Array zu erstellen.

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 

Aber wenn ich ein Array machen, und weisen Sie das Objekt funktioniert es gut

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 ist eine heikle Funktion mit dtype=object zu verwenden. Vergleichen Sie np.array([[1,2],[2,3]],dtype=object) und np.array([[1,2],[2]],dtype=object). Eins ist (2,2) das andere (2,). Es versucht, ein 2d-Array zu erstellen, und greift nur dann auf 1d mit Listenelementen zurück, wenn das fehlschlägt. Etwas in dieser Richtung passiert hier.

Ich sehe 2 Lösungen - eine ist diese Runde über den Aufbau der Array, die ich bei anderen Gelegenheiten verwendet habe. Der andere ist herauszufinden, warum np.array nicht in dict graben, aber mit Ihren tut. np.array ist kompiliert, so dass das Lesen harten GITHUB-Code erfordert.


habe ich versucht, eine Lösung mit f=np.frompyfunc(lambda x:x,1,1), aber das funktioniert nicht (meine Versionsgeschichte für Details). Aber ich fand, dass mit einem dict ein Extendeddict Mischen funktioniert:

In [139]: np.array([eb,basic]) 
Out[139]: array([{'test': {'field': 'test'}}, {'test.field': 'test'}], dtype=object) 

So hat Mischen mit etwas anderem wie None oder einer leeren Liste

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) 

Dieser anderen gemeinsamen Trick besteht darin, für die Konstruktion eines Objekt Array von Listen.

Es funktioniert auch, wenn Sie es zwei oder mehr Extendeddict mit unterschiedlichen Längen geben

np.array([eb, Extendeddict({})]). Mit anderen Worten, wenn sich len(...) unterscheiden (genau wie bei gemischten Listen).

+0

Leider passiert das gleiche, wenn ich das Argument 'dtype' entferne. :( –

+0

Das Problem ist nicht das 'dtype = Objekt'. Ich denke, es analysiert die Eingabe vor dem Blick auf den' dtype'. Von seinem Verhalten denke ich, schaut nur auf den 'dtype' nahe dem Ende, wenn das Konstruieren der Ergebnis – hpaulj

+0

Ich habe die gleichen Dinge wie Sie versucht, indem Sie ein Objekt mit anderer Länge hinzugefügt haben, das so funktioniert, wie Sie es beschreiben, aber das bedeutet auch, dass jeder, der diese Bibliothek benutzt, sich dieses Problems bewusst sein muss Ich behalte es so wie es jetzt ist, aber ich werde deine Antwort für den Fall aufpeppen, dass jemand anderes auf das gleiche Problem stößt. –

2

Numpy versucht zu tun, was es tun soll:

Numpy Kontrollen für jedes Element, wenn es iterable ist (durch len und iter verwendet wird), weil, was Sie passieren in als mehrdimensionales Array interpretiert werden kann.

Es gibt einen Haken hier: dict-ähnliche Klassen (dh isinstance(element, dict) == True) werden nicht als eine andere Dimension interpretiert (deshalb funktioniert die Weitergabe in [{}] funktioniert). Wahrscheinlich sollten sie überprüfen, ob es eine collections.Mapping anstelle einer dict ist. Vielleicht können Sie einen Fehler auf ihrem issue tracker ablegen.

Wenn Sie Ihre Klassendefinition ändern:

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

oder Ihre __len__ -Methode ändern:

def __len__(self): 
     raise NotImplementedError 

es funktioniert. Keiner von diesen könnte etwas sein, das Sie tun möchten, aber numpy verwendet nur Ente Eingabe, um das Array zu erstellen und ohne Unterklasse direkt von dict oder len unzugänglich macht numpy sieht Ihre Klasse als etwas, das eine andere Dimension sein sollte. Dies ist ziemlich clever und praktisch, wenn Sie benutzerdefinierte Sequenzen (Unterklassen von collections.Sequence) übergeben möchten, aber für collections.Mapping oder collections.MutableMapping unbequem sind. Ich denke, das ist ein Bug.

+0

Ich habe versucht, von "dict" zu erben, aber das verursacht eine Reihe anderer Probleme, die ich nicht herausfinden konnte, wie man richtig löst, aber ich denke auch, dass es ein Fehler in numpy selbst sein könnte. –

+0

@ NicolauGonçalves Ich wollte nicht empfehlen, von 'dict' zu erben. Es war nur um zu veranschaulichen, warum ich zu dem Schluss kam. – MSeifert

+0

Wie ich in einem Kommentar zu der anderen Antwort erwähnt habe, wäre es nicht kontraproduktiv, die Länge nicht zu definieren, wenn jemand diese Klasse verwenden würde. Aber ich werde ein Problem in numpy erstellen und sehen, was die Entwickler denken. –

Verwandte Themen