Dict. aber Reihenfolge der Keys() wichtig :(

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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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!
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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 ...
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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
[code]#!/usr/bin/env python
import this[/code]
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

(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...
Treehorn
User
Beiträge: 2
Registriert: Montag 8. November 2004, 09:29

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.
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Gegenvorschlag: bau dir ne Klasse, die von dict abgeleitet und setz Dookies Variante um. Dann brauchen nur einige wenige Methoden überschrieben werden...
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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:
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

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
Zuletzt geändert von Milan am Mittwoch 17. November 2004, 13:45, insgesamt 2-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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...
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

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" })
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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???
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Verbessert, so müsste es gehen... wenn schon, denn schon. Soll ja was wiederverwendbares sein :wink:
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ahhh Halt... Ich sehe gerade, das die veränderung eines Wertes die Reihenfolge durcheinander bring :(

Code: Alles auswählen

MySortedDict["Key2"]="Wert2XX"
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

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).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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
[code]#!/usr/bin/env python
import this[/code]
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

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...
Antworten