Seite 1 von 1

defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 18:17
von uweddf
Hallo,

ich möchte ein defaultdict(int) zu ein List-Objekt konvertieren.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import defaultdict

# 1. Versuch
a = defaultdict(int)
a[2] = 20
a[4] = 40
a[9] = 90

print ' >> [1] defaultdict in  ->', a.items()

b = [a[item]  for item in xrange(sorted(a.keys(), reverse=True)[0] +1)]

print ' >> [2] defaultdict out ->', a.items()
print ' >> [3] liste       out ->', b
print ' >>     ------------------'


# 2. Versuch
a = defaultdict(int)
a[2] = 20
a[4] = 40
a[9] = 90

print ' >> [4] defaultdict in  ->', a.items()

b = [0  for item in xrange(sorted(a.keys(), reverse=True)[0] +1)]

print ' >> [5] liste       out ->', b

for index, value in a.iteritems():
    b[index] = value

print ' >> [6] defaultdict out ->', a.items()
print ' >> [7] liste       out ->', b


# >> [1] defaultdict in  -> [(9, 90), (2, 20), (4, 40)]
# >> [2] defaultdict out -> [(0, 0), (1, 0), (2, 20), (3, 0), (4, 40), (5, 0), (6, 0), (7, 0), (8, 0), (9, 90)]
# >> [3] liste       out -> [0, 0, 20, 0, 40, 0, 0, 0, 0, 90]
# >>     ------------------
# >> [4] defaultdict in  -> [(9, 90), (2, 20), (4, 40)]
# >> [5] liste       out -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# >> [6] defaultdict out -> [(9, 90), (2, 20), (4, 40)]
# >> [7] liste       out -> [0, 0, 20, 0, 40, 0, 0, 0, 0, 90]
Beim ersten Versuch erhalte ich wie gewünscht ein List-Objekt, aber das defaultdict Objekt hat sich verändert, welches eigentlich nicht gewollt war.
>> defaultdict in : [(9, 90), (2, 20), (4, 40)]
>> defaultdict out : [(0, 0), (1, 0), (2, 20), (3, 0), (4, 40), (5, 0), (6, 0), (7, 0), (8, 0), (9, 90)].

Beim zweiten Versuch erhalte ich ebenfalls das gewünschte List-Objekt, ohne jedliche Veränderungen des defaultdict Objektes.

Kann ich wie beim ersten Versuch das veränderte defaultdict Objekt einfach ignorieren, oder handele ich mir irgend welche Nachteile dabei ein ?
Bitte berücksichtigt, das dies hier nur ein Beispiel ist und ein defaultdict(int) einige Hundert Keys enthalten kann und wenn dann alle fehlenden Keys mit einem '0-Value' ergänzt werden, das defaultdict um ein vielfaches größer werden kann.

Vielen Dank für Eure Hilfestellungen.

Re: defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 18:24
von EyDu
Was hast du dir denn mit dem xrange und dem sorted gedacht (für das Maximum gibt es die max-Funktion)? Wie stellst du dir es denn vor, wenn der kleinste Schlüssel negativ ist oder der größte irgendwo bei 10^100? Oder was ist, wenn die Schlüssel Strings sind? In Python kannst du direkt über Elemente iterieren ``for key in a.keys()`` oder ``for key, value in a.items()``. In diesem Fall geht es aber auch viel einfacher:

Code: Alles auswählen

list(a.items())

Re: defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 18:34
von pillmuncher
Zuerst das hier:

Code: Alles auswählen

sorted(a.keys(), reverse=True)[0] +1
Normalerweise bin ich gerne für Rube-Goldberg-Maschinen zu haben, aber an dieser Stelle würde ich doch lieber max() verwenden:

Code: Alles auswählen

max(a) + 1
Zum eigentlichen Problem: Die von dir nicht gewollte Veränderung des defaultdicts ist genau das Verhalten, weswegen man ein defauldict überhaupt verwendet. Wenn du dieses Verhalten nicht möchtest, dann nimm kein defaultdict. Statt dessen nimm ein normales dict und dessen get() Methode:

Code: Alles auswählen

# 3. Versuch
a = {}
a[2] = 20
a[4] = 40
a[9] = 90

print ' >> [1] defaultdict in  ->', a.items()

b = [a.get(item, 0) for item in xrange(max(a) + 1)]

print ' >> [2] defaultdict out ->', a.items()
print ' >> [3] liste       out ->', b
print ' >>     ------------------'

# >> [1] defaultdict in  -> [(9, 90), (2, 20), (4, 40)]
# >> [2] defaultdict out -> [(9, 90), (2, 20), (4, 40)]
# >> [3] liste       out -> [0, 0, 20, 0, 40, 0, 0, 0, 0, 90]
# >>     ------------------
Wenn du jedoch das defaultdict aus anderen Gründen brauchst, aber an dieser Stelle hier das Verhalten eines normalen dicts benötigst, dann kannst du das ebenso nehmen, sofern du hier ebenfalls die get() Methode verwendest und nicht den Subskriptionsoperator (d.i. das ...[...]).

Re: defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 19:24
von uweddf
@pillmuncher,

vielen Dank für Deine Antwort. Deine Lösung ist exakt diese, welche ich gesucht habe.

Ich werde mich bemühen in Zukunft den Bau von "Rube-Goldberg-Maschinen" zu vermeiden, da diese nicht wirklich Spass beim betrachten vermitteln.

Das max(a) == sorted(a.keys(), reverse=True)[0] ist, habe ich jetzt gelernt.

Danke.

Re: defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 19:30
von uweddf
@EyDu

vielen Dank für Deine schnelle Antwort.

Ich werde den Ausführungen von pillmuncher folgen. Meine Frage ist somit beantwortet.

Nur zur Info :

Die Datenstruktur des defaultdict(int) ist wie folgt :
- Alle Keys/Schlüssel sind ohne jedliche Ausnahmen immer postive Zahlen, im Bereich von 0 bis unendlich.
- Zum Zeitpunkt des konvertierens ist der größte Schlüssel nicht bekannt.

Die Struktur des vorhandenen desfaultdict soll in ein List-Objekt konvertiert werden.

[1] defaultdict a -> [(9, 90), (2, 20), (4, 40)]
[2] liste b -> [0, 0, 20, 0, 40, 0, 0, 0, 0, 90]

im Ergebnis soll a[9] == b[9] ergeben.

Vielen Dank für Deine Antwort.

Re: defaultdict(int) zu einem List-Objekt konvertieren

Verfasst: Sonntag 2. Februar 2014, 19:56
von EyDu
Da habe ich wohl ganz übersehen, dass du die Nullen tatsächlich beabsichtigst. Vielleicht die Frage mal von der anderen Seite betrachtet: Brauchst du die Daten überhaupt in einer Liste? Damit sammelst du nur jede Menge redundanter Daten. Reicht vielleicht einfach schon eine Zugriffsfunktion:

Code: Alles auswählen

>>> accessor = lambda index: a.get(index, 0)                                                              
>>> accessor(0)                                                                                                            
0                                                                                                                          
>>> accessor(1)                                                                                                           
0                                                                                                                          
>>> accessor(2)                                                                                                           
20
Oder ein wenig mehr an eine Liste angelehnt:

Code: Alles auswählen

>>> class Accessor(object):                                                                                                
...     def __init__(self, data, default):
...         self.data = data                                                                                               
...         self.default = default
...     def __len__(self):                                                                                                 
...         return max(self.data.keys())+1                                                                                 
...     def __getitem__(self, index):
...         return self.data.get(index, self.default)
...                                                                                                                        
>>> accessor = Accessor(a, 0)
>>> len(accessor)
10
>>> accessor[0]
0
>>> accessor[1]
0
>>> accessor[2]
20
Oder noch mehr an eine Liste angelehnt:

Code: Alles auswählen

>>> class IterableAccessor(Accessor):
...     def __iter__(self):
...         return (self.data.get(i, self.default) for i in xrange(len(self)))
... 
>>> iaccessor = IterableAccessor(a, 0)
>>> for index, value in enumerate(iaccessor):
...     print index, value
... 
0 0
1 0
2 20
3 0
4 40
5 0
6 0
7 0
8 0
9 90
>>> list(iaccessor)
[0, 0, 20, 0, 40, 0, 0, 0, 0, 90]
Unter Umständen bietet sich noch das Werfen eindes ``IndexError``s an, das hängt natürlich von deinem Vorhaben ab.