re suche in dicts - nicht nachvollziehbare ergebnisse

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Hallo,

ich habe folgende Klasse, die für mich meine Dictionaries verwalten soll.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
import re
class DataHandler:
    def __init__(self):
        self.data=dict()
        
    def addData(self,cat,data):
        self.data[cat][len(self.data[cat])]=data
    def addCategory(self,cat):
        self.data[cat]=dict()
        
    def delData(self,id_set):
        for cat,id_list in id_set.items():
            for id in id_list:
                del self.data[cat][id]
        
    def searchData(self,search):
        return_set=dict()
        r=re.compile(r'(\W)*'+search+'(\W)*',re.IGNORECASE)
         
        for cat in self.data.keys():
            return_set[cat]=list()
            for id in self.data[cat].keys():
                for value in self.data[cat][id].values():
                    if r.search(value):
                        return_set[cat].append(id)
            
        return return_set
        
    def getWholeData(self):
        return self.data
        
    def getDataId(self,search):
        return self.searchData(search)

dh=DataHandler()
data = {
    1: {"sprache1": u"Vornam (öäü)", "sprache2": u"first name"},
    2: {"sprache1": u"Nachnam (öäü)", "sprache2": u"last name"},
    3: {"sprache1": u"Tisch (öäü)", "sprache2": u"Table"},
    4: {"sprache1": u"Tabelle (öäü)", "sprache2": u"Table"},
}
dh.addCategory('vocab')
dh.addCategory('abc')
dh.addData('vocab',data[1])
dh.addData('vocab',data[2])
dh.addData('vocab',data[3])
dh.addData('abc',data[3])
dh.addData('vocab',data[4])

print dh.getDataId(u'last name')
print dh.getDataId(u'Table')
print dh.getDataId(u'name')
print '\n','-'*10,'Suche beendet','-'*10,'\n'

print dh.getWholeData()
print '\n','-'*10,'-'*10,'\n'

del_set1=dh.getDataId(u'Table')
dh.delData(del_set1)

print dh.getWholeData()
Funktioniert soweit so gut auch alles ganz prächtig. Das einzige, was mich verwirrt ist die Ausgabe:

Code: Alles auswählen

python dictest.py
{'vocab': [1], 'abc': []}
{'vocab': [2, 3], 'abc': [0]}
{'vocab': [0, 1], 'abc': []}

---------- Suche beendet ----------

{'vocab': {0: {'sprache1': u'Vornam (\xf6\xe4\xfc)', 'sprache2': u'first name'},
 1: {'sprache1': u'Nachnam (\xf6\xe4\xfc)', 'sprache2': u'last name'}, 2: {'spra
che1': u'Tisch (\xf6\xe4\xfc)', 'sprache2': u'Table'}, 3: {'sprache1': u'Tabelle
 (\xf6\xe4\xfc)', 'sprache2': u'Table'}}, 'abc': {0: {'sprache1': u'Tisch (\xf6\
xe4\xfc)', 'sprache2': u'Table'}}}

---------- ----------


---------- ----------

{'vocab': {0: {'sprache1': u'Vornam (\xf6\xe4\xfc)', 'sprache2': u'first name'},
 1: {'sprache1': u'Nachnam (\xf6\xe4\xfc)', 'sprache2': u'last name'}}, 'abc': {
}}
Die ersten beiden Ausgaben stimmen. Die Datensätze haben die ID 1 und 2,3, aber die letzte Suche sollte eigentlich 1 und 2 ausgeben. Nur irgendwie tut sie das nicht. Kann mir jemand sagen warum?

LG Tobi

P.S.:Wenn jemand vebresserungsvorschläge hat - immer her damit =)
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Hi Tobi!

Habe eben mal ein paar Minuten versucht da durchzublicken. Schaffe es um diese Uhrzeit nicht mehr so richtig.
Aber du solltest bedenken (vermutlich weißt du das), dass die Reihenfolge von Items in Dictionaries sich nicht daran orientiert, in welcher Reihenfolge du Items hinzufügst.

Mal sehen, ob ich morgen fitter bin :-)

Gute N8
JR
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Wieso? Stimmt doch alles. Du löscht alle Einträge von table. Da bleiben nur noch Vorname und Nachname übrig.
BlackJack

Ich verstehe auch das Problem nicht. Du hast ausserdem 'ne ganze Menge Ausgaben gepostet, vielleicht solltest Du mal sagen wo genau Du etwas anderes erwartest, was Du dort erwartest und warum.

Solche verschachtelten Datenstrukturen werden ziemlich schnell unübersichtlich, Du solltest Dir überlegen das ganze besser in Objekte zu kapseln mit Dokumentation und aussagekräftigen Methodennamen.

Ich habe versucht die Schleife in der Suchmethode mit weniger verschachtelten Indexzugriffen zu schreiben, weil Quelltext in der Regel verständlicher wird wenn man den Dingen Namen gibt, aber viel geholfen hat's nicht:

Code: Alles auswählen

        for category_name, category in self.data.iteritems():
            return_set[category_name] = list()
            for id_, vocables in category.iteritems():
                for vocable in vocables.itervalues():
                    if r.search(vocable):
                        return_set[category_name].append(id_)
                        break
Man beachte das ``break``: Das solltest Du unbedingt bei Dir einbauen, es sei denn Du willst wirklich eine ID mehrfach im Ergebnis haben, wenn mehrere Worte pro ID gefunden werden.

Bei der Ausgabe von verschachtelten Grunddatenstrukturen ist `pprint.pprint()` sehr nützlich um lesbarere Ausgaben zu bekommen.

Und Deine ID-Vergabe funktioniert nicht richtig, jedenfalls nicht wenn man löschen und hinzufügen von Einträgen in beliebiger Reihenfolge vornehmen darf. Beispiel: Man fügt 10 Vokabeln hinzu, die bekommen die IDs 0 bis 9. Dann löscht man eine Vokabel mit einer ID < 9 und fügt wieder eine hinzu -> die bekommt die ID 9 und überschreibt den vorhandenen Eintrag.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Es ging mir um folgende ausgabe:
python dictest.py
{'vocab': [1], 'abc': []}
{'vocab': [2, 3], 'abc': [0]}
{'vocab': [0, 1], 'abc': []}
eigentlich müsste beim letzten nicht 0,1 stehen sonder 1,2 wenn man sich mal die ids, die im dictionary stehen anschaut.
Aber im prinzip is es egal^^ Nach BlackJacks Hinweisen is mir aufgefallen, dass ich einige Designfehler drinnen hab. Aus dem Grund werd ich das alles nocheinmal überdenken und neuschreiben. Was bringt mir der Code, wenn (mal angenommen er tut es) richtig funzt, ich aber später hilfe brauch und ihr in nicht versteht - bzw zu lange braucht, um euch reinzudenken und ihr keine Lust drauf habt und deswegen keine Hilfe geben könnt? :wink:
Also auf ein neues. Ich meld mich.
Danke für die Hinweise und Hilfe =)
LG Tobi
BlackJack

CrackPod hat geschrieben:Es ging mir um folgende ausgabe:
python dictest.py
{'vocab': [1], 'abc': []}
{'vocab': [2, 3], 'abc': [0]}
{'vocab': [0, 1], 'abc': []}
eigentlich müsste beim letzten nicht 0,1 stehen sonder 1,2 wenn man sich mal die ids, die im dictionary stehen anschaut.
Warum? Das Dictionary sieht so aus (mit `pprint.pprint()` ausgegeben):

Code: Alles auswählen

{'abc': {0: {'sprache1': u'Tisch (\xf6\xe4\xfc)', 'sprache2': u'Table'}},
 'vocab': {0: {'sprache1': u'Vornam (\xf6\xe4\xfc)',
               'sprache2': u'first name'},
           1: {'sprache1': u'Nachnam (\xf6\xe4\xfc)',
               'sprache2': u'last name'},
           2: {'sprache1': u'Tisch (\xf6\xe4\xfc)', 'sprache2': u'Table'},
           3: {'sprache1': u'Tabelle (\xf6\xe4\xfc)', 'sprache2': u'Table'}}}
Du suchst nach '*name*' und das kommt unter ID 0 und 1 vor aber nicht unter ID 2.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Naja, ich habs jetz mal so gemacht, dass self.data ein dictionary ist, das listen enthält, die wiederum dictionaries mit den Daten enthalten.
So kann ich löschen und gefahrlos wieder einfügen(glaube ich derzeit zumindest :roll: ). Das ganze sieht so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import re

class DataHandler:
    def __init__(self):
        self.data=dict()

#-----------------------------------------------------------
    def addData(self,cat,data):
        '''
            Add new data to the list
        '''
        self.data[cat].append(data)

#-----------------------------------------------------------
    def addMany(self,cat,data_list):
        '''
            Add a list of data to the list
        '''
        for data_dict in data_list:
            self.data[cat].append(data_dict)

#-----------------------------------------------------------
    def addCategory(self,cat):
        '''
            Create a list which contains the data-dictionaries
        '''
        self.data[cat]=list()

#-----------------------------------------------------------
    def delData(self,id_set):
        '''
            Delete a member of one (or more) list by its index
        '''
        for cat,index_list in id_set.items():
            print cat,'-->'
            for index in index_list:
                print '\t',index,'-->',self.data[cat][index]
                del self.data[cat][index]

#-----------------------------------------------------------
    def searchData(self,search):
        '''
            Return the indexes of all data found by a regex
        '''
        return_set=dict()
        r=re.compile(r'(\W)*'+search+'(\W)*',re.IGNORECASE)
         
        for cat in self.data.keys():
            #create for every data-category a list for containing the indexes
            return_set[cat]=list()
            for data_dict in self.data[cat]:
                for value in data_dict.values():
                    #search in the data
                    if r.search(value):
                        return_set[cat].append(self.data[cat].index(data_dict))
            
        return return_set

#-----------------------------------------------------------
    def getWholeData(self):
        return self.data

#-----------------------------------------------------------
    def getDataIndex(self,search):
        return self.searchData(search)

dh=DataHandler()
data = [
    {u"sprache1": u"Vornam (öäü)", u"sprache2": u"first name"},
    {u"sprache1": u"Nachnam (öäü)", u"sprache2": u"last name"},
    {u"sprache1": u"Tisch", u"sprache2": u"Table"},
    {u"sprache1": u"Tabelle", u"sprache2": u"Table"},
]
dh.addCategory(u'vocab')
dh.addCategory(u'abc')
dh.addCategory(u'liste')

dh.addData(u'vocab',data[0])
dh.addData(u'vocab',data[1])
dh.addData(u'vocab',data[2])
dh.addData(u'vocab',data[3])
dh.addData(u'abc',data[2])
dh.addData(u'abc',data[3])
dh.addMany('liste',data)

print '--------------------------------------'
print dh.getDataIndex(u'Table')
dh.delData(dh.getDataIndex(u'Table'))
print '-----------'
print dh.getDataIndex(u'Table')
Nur habe ich beim löschen Probleme...
Wenn ich die Daten nich lösche sondern in der Funktion `delData` ausgeben lasse, geht alles. Sieht so aus:

Code: Alles auswählen

python dictest.py
--------------------------------------
{u'vocab': [2, 3], u'liste': [2, 3], u'abc': [0, 1]}
vocab -->
        2 --> {u'sprache1': u'Tisch', u'sprache2': u'Table'}
        3 --> {u'sprache1': u'Tabelle', u'sprache2': u'Table'}
liste -->
        2 --> {u'sprache1': u'Tisch', u'sprache2': u'Table'}
        3 --> {u'sprache1': u'Tabelle', u'sprache2': u'Table'}
abc -->
        0 --> {u'sprache1': u'Tisch', u'sprache2': u'Table'}
        1 --> {u'sprache1': u'Tabelle', u'sprache2': u'Table'}
-----------
{u'vocab': [2, 3], u'liste': [2, 3], u'abc': [0, 1]}
Wenn ich jedoch löschen will - was die funktion auch tun soll - dann bekomm ich nen IndexError. Sieht so aus:

Code: Alles auswählen

C:\Dokumente und Einstellungen\Tobsl\Eigene Dateien\Python>python dictest.py
--------------------------------------
{u'vocab': [2, 3], u'liste': [2, 3], u'abc': [0, 1]}
vocab -->
        2 --> {u'sprache1': u'Tisch', u'sprache2': u'Table'}
        3 -->
Traceback (most recent call last):
  File "dictest.py", line 91, in <module>
    dh.delData(dh.getDataIndex(u'Table'))
  File "dictest.py", line 40, in delData
    print '\t',index,'-->',self.data[cat][index]
IndexError: list index out of range
Nur weiß ich nich so genau, warum...

LG Tobi

Edit:
Wenn nur ein Datensatz pro Kategorie zum löschen gefunden wird,

Code: Alles auswählen

print '--------------------------------------'
print dh.getDataIndex(u'last name')
dh.delData(dh.getDataIndex(u'last name'))
print '-----------'
print dh.getDataIndex(u'last name')
dann geht es komischerweise. Verwirrt mich jetz noch mehr.

Code: Alles auswählen

python dictest.py
--------------------------------------
{u'vocab': [1], u'liste': [1], u'abc': []}
vocab -->
        1 --> {u'sprache1': u'Nachnam (\xf6\xe4\xfc)', u'sprache2': u'last name'
}
liste -->
        1 --> {u'sprache1': u'Nachnam (\xf6\xe4\xfc)', u'sprache2': u'last name'
}
abc -->
-----------
{u'vocab': [], u'liste': [], u'abc': []}
BlackJack

CrackPod hat geschrieben:

Code: Alles auswählen

Traceback (most recent call last):
  File "dictest.py", line 91, in <module>
    dh.delData(dh.getDataIndex(u'Table'))
  File "dictest.py", line 40, in delData
    print '\t',index,'-->',self.data[cat][index]
IndexError: list index out of range
Nur weiß ich nich so genau, warum...
Weil es den Listenindex nicht gibt an dem Du löschen möchtest. Angenommen ich möchte aus folgender Liste 'a' und 'c' löschen, also Indexe 0 und 2:

Code: Alles auswählen

In [59]: a = ['a', 'b', 'c']

In [60]: del a[0]

In [61]: del a[2]
---------------------------------------------------------------------------
exceptions.IndexError         Traceback (most recent call last)

/home/marc/<ipython console>

IndexError: list assignment index out of range

In [62]: a
Out[62]: ['b', 'c']
Nachdem ein Element gelöscht wurde, verändert sich der Index von allen nachfolgenden Elementen. Selbst wenn man dann keinen Index erwischt der ausserhalb liegt, also keine Ausnahme bekommt, kann das schiefgehen. Wenn man nämlich 'a' und 'b' also die Indices 0 und 1 auf diese Weise löschen will, entfernt man stattdessen 'a' und 'c':

Code: Alles auswählen

In [63]: a = ['a', 'b', 'c']

In [64]: del a[0]

In [65]: del a[1]

In [66]: a
Out[66]: ['b']
Letztendlich ist diese ganze ID-Geschichte IMHO keine gute Entwurfsidee. Warum hantierst Du indirekt mit ganzen Zahlen die so etwas wie Referenzen auf Vokabel-Objekte sind, anstatt direkt mit den Objekten zu arbeiten?

Anyway -- so kann man die Löschmethode umschreiben (ungetestet):

Code: Alles auswählen

    def delData(self,id_set): 
        ''' 
            Delete a member of one (or more) list by its index 
        ''' 
        for category, indices in id_set.iteritems():
            indices = set(indices)
            self.data[category] = [element for i, element
                                   in enumerate(self.data[category])
                                   if i not in indices]
Die Suchmethode ist immer noch ziemlich unübersichtlich mit der ganzen Indexiererei, besonders die letzte Zeile in den Schleifen ist furchtbar. Nicht nur weil dort tief in die Datenstruktur gegriffen wird, sondern weil der Index der Vokabel in linearer Zeit ermittelt wird, den man an der Stelle eigentlich schon in konstanter Zeit zur Verfügung haben kann wenn man die Vokabeln mit `enumerate()` "mitzählt":

Code: Alles auswählen

        for category, vocables in self.data.iteritems(): 
            return_set[category] = list() 
            for index, vocable in enumerate(vocables):
                for translation in vocable.values(): 
                    if r.search(translation): 
                        return_set[category].append(index)
                        break
Und das ``break`` fehlte wieder.
Antworten