Seite 1 von 2

Dict. aber Reihenfolge der Keys() wichtig :(

Verfasst: Montag 15. November 2004, 22:16
von jens
Es ist schon das zweite mal, das ich eigentlich für ein paar Daten (vCard-Informationen) ein Dict nehmen würde... Nun ist es aber bekanntermaßen so, das die Keys() eines Dicts "durcheinander" gespeichert werden...

Ist ja normalerweise kein Problem, da es ja Daten sind die man über die Keys() ansprechen will...

Aber was mache ich, wenn die Reihenfolge doch wichtig ist?

Zwei Möglichkeiten hab ich mir Ausgedacht:
* Die Keys() beim "schreiben" des Dicts zusätzlich in einer Liste packen
* Kein Dict sondern eine Liste in einer Liste nehmen:
[ ["key1", "Wert1"] , ["key1", "Wert1"] ]

Gegen die erste Variante spricht eigentlich der "Speicherplatz", denn es werden gleiche Informationen zwei mal abgespeichert...
Ist die zweite Variante damit die beste??? Die zweite Variante erlaubt aber nicht das Typische ansprechen wie beim Dict über einen Key... (Wobei das bei meinem VCards-Problem egal ist)

Verfasst: Montag 15. November 2004, 22:33
von CM
Hi Jens,

Zu Variante 2 (verschachtelte Listen; Listen mit Listen, Listen mit tuples, Listen mit dicts) habe ich selber schon gegriffen und Du kannst ja auch nach liste[0] sortieren.
Eine dritte Variante bietet Python2.4:

Code: Alles auswählen

>>> d = {1:'a',2:'b'}
>>> d = sorted(d.items())
Aber das ist wohl nicht unbedingt, was Du suchst.

Variante 1 ist wenig elegant, kann man aber ruhig nutzen - finde ich. Wenn Du nur eine Abfrage für ein "V-Card-Dict" machen möchtest mit einem Dutzend Einträgen: Warum nicht? Dann machst Du halt so was: l = [name, tel, ...]
und dann : for i in l: print dict oder so. Und l wäre dann eine "globale" Liste, die alle Funktionen kennen, die sie brauchen.

Gruß,
Christian

Verfasst: Montag 15. November 2004, 22:37
von jens
Ist es wirklich eine 2.4 neuheit, oder das selbe wie das:

Code: Alles auswählen

d = {1:'a',2:'b'}
k = d.keys()
k.sort()
print k
Also nur eine Alphabetisch sortierte Liste??? Darum dreht es sich bei mir nicht... Ich will die rheinfolge, mit der ich die Daten ins Dict geschrieben hab!

Verfasst: Montag 15. November 2004, 22:46
von CM
Das ist mir schon klar, das Du nach was anderem suchtest. Aber dann mußt Du halt - nach meinem Wissen - ausweichen auf eine der anderen Alternativen.
sorted ist neu, version 2.3 kennt es nicht. Wenn nur eine sortierte Liste der Schlüssel herauskommt, wie in Deinem Beispiel, muß man halt über diese Liste die Abfrage im dict machen. Ist natürlich genauso gut - und genauso an Deiner Frage vorbei.

Ich dachte mir halt, daß ich das auch mal in die Runde werfe ...

Verfasst: Montag 15. November 2004, 23:32
von Dookie
Hi jens,

ich würd das so machen:

Code: Alles auswählen

>>> mykeys = ["erster", "zweiter", "dritter", "vierter"]

>>> mydict = dict(zip(mykeys, [1,2,3,4]))

>>> print mydict
{'vierter': 4, 'dritter': 3, 'erster': 1, 'zweiter': 2}

>>> print keys[1], mydict[keys[1]]
zweiter 2
So hast Du die Schlüssel nur einmal definiert und hast ihre reihenfolge, ohne Redundanzen, da die Strings in den Schlüsseln des Dictionaries die gleichen (identische Instanzen) wie die Strings in der Liste sind.


Gruß

Dookie

Verfasst: Dienstag 16. November 2004, 09:10
von jens
(in deinem Listing muß es mykeys und nicht nur keys heißen)

Ich hab deine Variante erst mal nicht verstanden, aber damit wurde mir es klar:

Code: Alles auswählen

mykeys = ["erster", "zweiter", "dritter", "vierter"]
myvalues = ["Wert1", "Wert2", "Wert3", "Wert4"]

mydict = dict(zip(mykeys, myvalues ))

print mydict.keys()[0]
print mykeys[3]

print id( mydict.keys()[0] )
print id( mykeys[3] )

for i in xrange( len(mydict) ):
    print mykeys[i], mydict[mykeys[i]]

Code: Alles auswählen

vierter
vierter
9221728
9221728
erster Wert1
zweiter Wert2
dritter Wert3
vierter Wert4
Nur, ob das jetzt einfacher als eine Liste in einer Liste ist, weiß ich noch nicht...

Verfasst: Dienstag 16. November 2004, 11:19
von Treehorn
Speicher die Keys doch einfach von den Values getrennt in 2 Listen.
Zurgriff dann einfach so:

Code: Alles auswählen

keys = ["eins", "zwei", "drei", "vier", "fuenf", "sechs"]
vals = [1, 2, 3, 4, 5, 6]

beliebigeVariable = vals[keys.index("zwei")]
Wenn Du dir ne schöne Klasse draus machst und die entsprechenden Operatoren überschreibst sowie ne vernünftige Sammlung von Methoden zusammenstellst mit denen Du die "Paare" hinzufügen umstellen und dergleichen kannst, haste gleich was für die Zukunft.

Verfasst: Dienstag 16. November 2004, 18:12
von Milan
Hi. Gegenvorschlag: bau dir ne Klasse, die von dict abgeleitet und setz Dookies Variante um. Dann brauchen nur einige wenige Methoden überschrieben werden...

Verfasst: Dienstag 16. November 2004, 19:08
von Gast
Stimmt, ist schöner.

Verfasst: Dienstag 16. November 2004, 20:28
von jens
Milan hat geschrieben:Hi. Gegenvorschlag: bau dir ne Klasse, die von dict abgeleitet und setz Dookies Variante um. Dann brauchen nur einige wenige Methoden überschrieben werden...
Hmmm... Da weiß ich allerdings garnicht wo ich anfangen soll... Übersteigt wohl meine Kompetenzen :oops:

Verfasst: Dienstag 16. November 2004, 21:09
von Milan
Hi. Mal ein kleiner Versuch, etwas wiederverwendbares zu schaffen, denn das Konzept an sich geht für ein Script einfacher zu verwirklichen:

Code: Alles auswählen

class SortedDict(dict):
    __slots__=["__keylist"]
    def __init__(self,tuplelist=(),**kw):
        dict.__init__(self)
        self.__keylist=[]
        for k,v in tuplelist:
            self[k]=v
        self.update(kw)
    def __iter__(self):
        return iter(self.__keylist)
    def __delitem__(self,k):
        dict.__delitem__(self,k)
        self.__keylist.remove(k)
    def __setitem__(self,k,v):
        dict.__setitem__(self,k,v)
        if k in self.__keylist:
            self.__keylist.remove(k)
        self.__keylist.append(k)
    def clear(self):
        dict.clear(self)
        del self.__keylist[:]
    def copy(self):
        new=SortedDict()
        for k in self:
            new[k]=self[k]
        return new
    def items(self):
        return [(k,self[k]) for k in self]
    def iteritems(self):
        for k in self:
            yield (k,self[k])
    iterkeys=__iter__
    def itervalues(self):
        for k in self:
            yield self[k]
    def keys(self):
        return self.__keylist[:]
    def pop(self,k,d=None):
        v=dict.pop(k,d)
        try:
            self.__keylist.remove(k)
        except ValueError:
            pass
        return v
    def popitem(self):
        k,v=dict.popitem(self)
        try:
            self.__keylist.remove(k)
        except ValueError:
            pass
        return k,v
    def setdefault(self,k,d=None):
        erg=self.get(k,d)
        if k not in self:
            self[k]=d
        return erg
    def update(self,otherdict):
        for k,v in otherdict.iteritems():
            self[k]=v
    def values(self):
        return [self[k] for k in self]

    def fromkeys(self,S,v=None):
        new=SortedDict()
        for k in S:
            new[k]=v
        return new

Verfasst: Dienstag 16. November 2004, 21:31
von jens
Wow, was für'n Listing... Da verstehe ich erstmal nix... Aber mal Probieren:

Code: Alles auswählen

MySortedDict = SortedDict()

MySortedDict["Key1"]="Wert1"
MySortedDict["Key2"]="Wert2"

print MySortedDict
print MySortedDict.items()

for i in MySortedDict.keys():
    print i,"-",MySortedDict[i]

Code: Alles auswählen

{'Key2': 'Wert2', 'Key1': 'Wert1'}
[('Key1', 'Wert1'), ('Key2', 'Wert2')]
Key1 - Wert1
Key2 - Wert2
Nicht schlecht... Ist eine wirklich brauchbare Lösung...


Wobei das geht nicht so richtig:

Code: Alles auswählen

MySortedDict = SortedDict()

MySortedDict={ "Key0":"Wert0", "Key1":"Wert1" }
print MySortedDict

MySortedDict["Key2"]="Wert2"
MySortedDict["Key3"]="Wert3"
MySortedDict["Key4"]="Wert4"

for i in MySortedDict.keys():
    print i,"-",MySortedDict[i]

Code: Alles auswählen

{'Key1': 'Wert1', 'Key0': 'Wert0'}
Key3 - Wert3
Key2 - Wert2
Key1 - Wert1
Key0 - Wert0
Key4 - Wert4
Aber damit kann man leben...

Verfasst: Dienstag 16. November 2004, 21:33
von Milan
Hi. Hmmm, da muss ich mir noch aml die Init anschauen. Aber ansonsten mach doch sowas:

Code: Alles auswählen

MySortedDict = SortedDict()
MySortedDict.update({ "Key0":"Wert0", "Key1":"Wert1" })

Verfasst: Dienstag 16. November 2004, 21:38
von jens
Nur keine Mühe, mir reicht eigentlich schon das:

Code: Alles auswählen

class SortedDict(dict):

    __slots__=["__keylist"]

    def __init__(self,*args,**kw):
        dict.__init__(self,*args,**kw)
        self.__keylist=[]

    def __iter__(self):
        return iter(self.__keylist)

    def __setitem__(self,k,v):
        dict.__setitem__(self,k,v)
        if k in self.__keylist:
            self.__keylist.remove(k)
        self.__keylist.append(k)
        
    def keys(self):
        return self.__keylist[:]

Code: Alles auswählen

MySortedDict = SortedDict()

MySortedDict["Key1"]="Wert1"
MySortedDict["Key2"]="Wert2"
MySortedDict["Key3"]="Wert3"

for i in MySortedDict.keys():
    print i,"-",MySortedDict[i]


MySortedDict["Key2"]="Wert2XX"

MySortedDict["Key4"]="Wert4"
MySortedDict["Key5"]="Wert5"

print "="*15
for i in MySortedDict.keys():
    print i,"-",MySortedDict[i]

Code: Alles auswählen

Key1 - Wert1
Key2 - Wert2
Key3 - Wert3
===============
Key1 - Wert1
Key3 - Wert3
Key2 - Wert2XX
Key4 - Wert4
Key5 - Wert5
Hab nach und nach die Funktionen rauskommentiert und stets geprüft, ob das Ergebnis noch stimmt... Aber kann man es noch weiter zusammen kürzen???

Verfasst: Dienstag 16. November 2004, 21:39
von Milan
Verbessert, so müsste es gehen... wenn schon, denn schon. Soll ja was wiederverwendbares sein :wink:

Verfasst: Dienstag 16. November 2004, 21:40
von jens
Ahhh Halt... Ich sehe gerade, das die veränderung eines Wertes die Reihenfolge durcheinander bring :(

Code: Alles auswählen

MySortedDict["Key2"]="Wert2XX"

Verfasst: Dienstag 16. November 2004, 21:45
von Milan
Hi. Ja, das hab ich mir so gedacht. Ansonsten musst du halt __setitem__ verändern, dass der wert nicht gelöscht wird, falls vorhanden (und dann aber auch nicht noch hinzugefügt wird).

Verfasst: Dienstag 16. November 2004, 21:49
von jens
Thx.... So geht's:

Code: Alles auswählen

class SortedDict(dict):

    __slots__=["__keylist"]

    def __init__(self,*args,**kw):
        dict.__init__(self)
        self.__keylist=[]

    def __iter__(self):
        return iter(self.__keylist)

    def __setitem__(self,k,v):
        dict.__setitem__(self,k,v)
        if not k in self.__keylist:
            self.__keylist.append(k)

    def keys(self):
        return self.__keylist[:]

Code: Alles auswählen

Key1 - Wert1
Key2 - Wert2
Key3 - Wert3
===============
Key1 - Wert1
Key2 - Wert2XX
Key3 - Wert3
Key4 - Wert4
Key5 - Wert5

Verfasst: Dienstag 16. November 2004, 22:20
von Dookie
Hi jens,

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
    Modul:          Lict
    Description:    Mix of list and dict
    Version:        0.1
    Copyright:      2004 by Fritz Cizmarov fritz@sol.at
    Created:        16. Nov. 2004
    Last modified:  16. Nov. 2004
    License:        free
    Requirements:   Python2.3
    Exports:        Classes and Functions to export
"""


class Index(int):
    """ Helperclass for access Lict by Index """
    __slots__ = []

class Lict(dict):
    
    __slots__ = ["__keylist"]
    
    def __init__(self, *args, **kw):
        super(Lict, self).__init__(*args, **kw)
        self.__keylist = []
        if len(args) == 1 and isinstance(args[0], (tuple, list)):
            for key, value in args[0]:
                self.__keylist.append(key)
        for key in kw:
            self.__keylist.append(key)
    
    def __getitem__(self, key):
        if type(key) is Index:
            key = self.__keylist[key]
        return super(Lict, self).__getitem__(key)

    def __setitem__(self, key, value):
        """ sets item, if item not exists append it """
        if type(key) is Index:
            key = self.__keylist[key]
        elif key not in self.__keylist:
            self.__keylist.append(key)
        super(Lict, self).__setitem__(key, value)

    def __delitem__(self, key): 
        if type(key) is Index: 
            key = self.__keylist[key] 
        super(Lict, self).__delitem__(key) 
        del self.__keylist[key]

    def __str__(self): 
        tpl = "{"+("%s : %s, "*len(self.__keylist))[:-2]+"}" 
        if self.__keylist: 
            data = [repr(x) for x in reduce(lambda a, b: a+b, self.iteritems())] 
            return tpl % tuple(data) 
        else: 
            return tpl 

    def __repr__(self): 
        tpl = "Lict(["+("(%s, %s), "*len(self.__keylist))[:-2]+"])" 
        if self.__keylist: 
            data = [repr(x) for x in reduce(lambda a, b: a+b, self.iteritems())] 
            return tpl % tuple(data) 
        else: 
            return tpl
    def __iter__(self):
        return iter(self.__keylist)

    def iteritems(self):
        for key in self.__keylist:
            yield key, self[key]

    iterkeys = __iter__

    def itervalues(self):
        for key in self.__keylist:
            yield self[key]
            
    def update(self, E):
        if not isinstance(E, Lict):
            raise TypeError("E is not from type Lict!")
        for key in E:
            self[key] = E[key]
    
    def pop(self, key=None):
        if key is None:
            key = self.__keylist[-1]
        elif type(key) is Index:
            key = self.__keylist[key]
        self.keylist.remove(key)
        return super(Lict, self).pop(key)

    def reverse(self):
        self.__keylist.reverse()

    def sort(self):
        self.__keylist.sort()

    def insert(self, index, key, value): 
        self.__keylist.insert(index, key) 
        super(Lict, self).__setitem__(key, value)

if __name__ == "__main__":
    a = Lict((('a', "eins"),('b', "zwei"), ('c', "drei")))
    print a
    a[Index(1)]="neu"
    print a
    a["d"] = "ganzneu"
    print a
    for i in xrange(len(a)):
        print a[Index(i)],
    for i in a:
        print i,
Hab auch mal was gebastelt. Index ist eine Hilfsklasse um auch über den Index auf die Einträge im Dictionary zugreifen zu können.


Gruß

Dookie

Edit by Milan: Änderungen von Dookie zwecks Übersichtlichkeit eingefügt

Verfasst: Mittwoch 17. November 2004, 13:48
von Milan
Hi. Nett, aber nicht ganz in Ordnung:

Code: Alles auswählen

>>> Lict()

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in -toplevel-
    Lict()
  File "<pyshell#1>", line 40, in __repr__
    data = [repr(x) for x in reduce(lambda a, b: a+b, self.iteritems())]
TypeError: reduce() of empty sequence with no initial value
>>> Lict({"a":"b"})

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in -toplevel-
    Lict({"a":"b"})
  File "<pyshell#1>", line 40, in __repr__
    data = [repr(x) for x in reduce(lambda a, b: a+b, self.iteritems())]
TypeError: reduce() of empty sequence with no initial value
Kann man außerdem nicht das superobj als attribut speichern? Sonst muss es ja jedesmal erzeugt werden...