defaultdict(int) zu einem List-Objekt konvertieren

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
uweddf
User
Beiträge: 12
Registriert: Freitag 30. August 2013, 10:28

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.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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())
Das Leben ist wie ein Tennisball.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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 ...[...]).
In specifications, Murphy's Law supersedes Ohm's.
uweddf
User
Beiträge: 12
Registriert: Freitag 30. August 2013, 10:28

@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.
Zuletzt geändert von uweddf am Sonntag 2. Februar 2014, 19:32, insgesamt 1-mal geändert.
uweddf
User
Beiträge: 12
Registriert: Freitag 30. August 2013, 10:28

@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.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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.
Das Leben ist wie ein Tennisball.
Antworten