Seite 1 von 2
list of dictionary, erstellen durch kopieren von Referenzen
Verfasst: Dienstag 16. März 2010, 16:32
von loonquawl
Guten Tag.
Ich habe ein Programm geschrieben, in dem der User Daten erzeugt. Diese sind immer aus den selben Kategorien ('Datei', 'xWert', ... usw), und ich dachte mir, es wäre schön sie in einer list of dictionaries abzulegen
ld = [{'d':1,'x'=34},{'d':56,'x'=76},...]
'Das Internet' suggerierte mir, ich könne dann z.b. ld[300][x] aufrufen, um an den x Wert aus dem 300sten Eintrag der list of dictionaries zu kommen.
Leider ist das Verhalten sehr erratisch, und ich komme nicht dahinter, was ich falsch mache.
Hier ein Beispiel:(Man beachte, dass ich am Ende nur einmal das dictionary s appende, und trotzdem alle dictionaries ersetzt werden...)
System: XP32, Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)]
Code: Alles auswählen
>>> q={'a':1,'b':2}
>>> s={'a':8,'b':9}
>>> ld = []
>>> ld.append(q)
>>> ld.append(s)
>>> ld
[{'a': 1, 'b': 2}, {'a': 8, 'b': 9}]
>>> s={'a':5,'b':6}
>>> ld
[{'a': 1, 'b': 2}, {'a': 8, 'b': 9}]
>>> ld.append(s)
>>> ld
[{'a': 1, 'b': 2}, {'a': 8, 'b': 9}, {'a': 5, 'b': 6}]
>>> ld[0]['a']
1
>>> ld=[]
>>> ld.append(s)
>>> ld.append(s)
>>> ld
[{'a': 5, 'b': 6}, {'a': 5, 'b': 6}]
>>> s['a']=100
>>> ld.append(s)
>>> ld
[{'a': 100, 'b': 6}, {'a': 100, 'b': 6}, {'a': 100, 'b': 6}]
>>>
Über Tips zu diesem Verhalten bin ich dankbar.
Re: list of dictionaries - Werte einzeln ansprechen missling
Verfasst: Dienstag 16. März 2010, 16:54
von numerix
loonquawl hat geschrieben:Leider ist das Verhalten sehr erratisch,
Erklärung: Du hängst immer nur weitere Referenzen des immer gleichen Dictionaries an deine Liste an. Änderst du etwas an diesem Dictionary, dann zeigen die Referenzen diese Änderung natürlich auch an.
Verfasst: Dienstag 16. März 2010, 16:54
von cofi
Hallo und willkommen im Forum!
Aber sei in deinen Problembeschreibungen in Zukunft doch bitte genauer. Dein Problem ist ja nicht das zugreifen auf Dictionaries, sondern dass du immer mit demselben Dictionary arbeitest.
(Es ist uebrigens das 301. Element, da die Zaehlung bei 0 beginnt.)
Verfasst: Dienstag 16. März 2010, 16:55
von Hyperion
Sehr löblich, dass Du das in einer Shell ausprobierst!
Probiere mal folgendes in der Shell:
Verfasst: Dienstag 16. März 2010, 17:15
von loonquawl
Danke für die schnellen Antworten!
Soweit ich das verstehe, ist also mein ld im Beipiel nur als s,s,s... gespeichert, und wird bei Änderung von s demnach betroffen.
Nun habe ich das Programm so geschrieben, dass die Entscheidungen des Anwenders immer in ein dictionary s geschrieben werden, das ich in jedem neuen Durchlauf mit ld.append(s) an meine Liste anhänge - wie könnte ich das umsetzen?
@cofi : Bei deiner Definition von 'Problem' hätte ich es nach 'richtiger' Formulierung auch selber lösen können..

-habe aber die Problembeschreibung modifiziert, damit die Nachwelt mehr davon hat...
@Hyperion : Das ist ja wirklich interessant! habe ein bisschen rumgegoogelt, und wundere mich nun, dass ich vorher noch nie in diese Referenz != Objekt Geschichte gestolpert bin.
Habe gleich ein bisschen weiter geshellt und bin mir des Verhaltens immer noch nicht sicher: wenn ich a=100 und b=100 sage, dann ist 'a is b' true, wenn ich aber händisch an meine Liste ld noch einen Eintrag anhänge, der den gleichen Inhalt hat wie 's', dann ist 'ld[0] is ld[3]' false....
Jedenfalls: Kann mir jemand raten, wie ich am schönsten ein Dictionary in eine Liste appende? also wie weise ich das Objekt, und nicht die Referenz zu?
Verfasst: Dienstag 16. März 2010, 17:37
von Rebecca
loonquawl hat geschrieben:wenn ich a=100 und b=100 sage, dann ist 'a is b' true,
Das ist nur ein Implementationsdetail von cpython zwecks Optimierung. Da man Integer ja nicht veraendern kann, ist es wurscht, dass hier die Objektidentitaet gleich ist, wo sie es eigentlich erstmal nicht sein sollte.
also wie weise ich das Objekt, und nicht die Referenz zu?
Gar nicht. Was du ja wohl willst, ist eine Kopie erzeugen, und darauf dann referenzieren. Kopien einer Liste s kannst du z.B. mit s[:] erzeugen. Je nachdem was deine Liste enthaelt, muesstest du dir auch deepcopy ansehen.
Verfasst: Dienstag 16. März 2010, 18:01
von numerix
Rebecca hat geschrieben:Was du ja wohl willst, ist eine Kopie erzeugen, und darauf dann referenzieren. Kopien einer Liste s kannst du z.B. mit s[:] erzeugen.
Wenn er überhaupt eine Kopie braucht, dann die der Dictionaries, das geht nicht mittels Slicing, da braucht er das copy-Modul. Gemäß der Beschreibung es OP erzeugt er allerdings ein Problem, das er gar nicht hat, wenn er das macht, was er vorhat:
Code: Alles auswählen
>>> d = {'A':1}
>>> ls = []
>>> ls.append(d)
>>> d = {'B':2}
>>> ls.append(d)
>>> ls
[{'A': 1}, {'B': 2}]
>>> d['B']=1
>>> ls
[{'A': 1}, {'B': 1}]
>>> d['A']=2
>>> ls
[{'A': 1}, {'A': 2, 'B': 1}]
Wenn du immer ein neues Dictionary erzeugst, und es immer wieder neu an den Namen s (oderwasweißich) bindest, dann sind es auch keine Referenzen, die du an die Liste anhängst ...
Verfasst: Dienstag 16. März 2010, 18:02
von ms4py
Wenn du x-Dictionaries hast, die immer die gleichen Keys haben, wäre eine eigener Datentyp in Form einer Klasse
deutlich eleganter und lesbarer.
Verfasst: Dienstag 16. März 2010, 18:06
von loonquawl
Gar nicht. Was du ja wohl willst, ist eine Kopie erzeugen, und darauf dann referenzieren. Kopien einer Liste s kannst du z.B. mit s[:] erzeugen. Je nachdem was deine Liste enthaelt, muesstest du dir auch deepcopy ansehen.
... leider will ich keine Liste, sondern ein dict kopieren; s{:} funktioniert nicht, deepcopy(s) funktioniert.
Mit Shell-Spielerei bin ich draufgekommen, dass es auch reicht nach jeder Zuweisung ld.append(s) einfach s=[] zu setzen. Auch wenn man später s wieder 'auffüllt', es inhaltlich also wieder dem alten s angleicht, ist es sozusagen 'neu', und führt nicht dazu, dass sich alle Einträge in ld auf ein und dasselbe Objekt beziehen.
Danke allen die helfen wollten!
edit: habe gerade den Beitrag von numerix gelesen - da ich im Programm bislang nur einzelne Einträge geändert habe, war mir das noch nicht aufgefallen, meine s=[] Lösung ist also das selbe, nur in umständlich

@ms4py: Ich hatte drüber nachgedacht, war aber zu faul....
Verfasst: Dienstag 16. März 2010, 18:14
von ms4py
loonquawl hat geschrieben:@ms4py: Ich hatte drüber nachgedacht, war aber zu faul....
Mit zwei Attributen (filename, x) sind das
ganze 4 Zeilen o_O
Und nachdem du zum dritten Mal ld[0].x anstatt ld[0]['x'] schreibst hättest du die Zeit schon wieder drin. Ich wär zu faul um ständig die zweite Lösung zu schreiben

Verfasst: Dienstag 16. März 2010, 18:46
von loonquawl
sind das ganze 4 Zeilen
wären das ganze 4 Zeilen, wenn ich wüsste wie es geht. Habe es gerade versucht, aber nachdem
Code: Alles auswählen
class LiDi(UserList):
def __init__(self, listpos, keyname, value):
UserList.__init__(self)
self[listpos][keyname] = value
... schon 4 Zeilen waren, und die neue Klasse damit noch nichts rausgibt...
Verfasst: Dienstag 16. März 2010, 19:04
von ms4py
Code: Alles auswählen
class MyDatatype(object):
def __init__(self, fname, x):
self.fname = fname
self.x = x
ld.append(MyDatatype('file', 5))
Verfasst: Dienstag 16. März 2010, 19:08
von jbs
Code: Alles auswählen
In [8]: from collections import namedtuple
In [9]: foo = namedtuple('foo', 'fname x')
In [10]: foo('fname', 5)
Out[10]: foo(fname='fname', x=5)
Warum dann nicht in 2 Zeilen?
Verfasst: Dienstag 16. März 2010, 19:26
von loonquawl
@ms4py : leider verstehe ich die Lösung nicht - wenn ich das so mache, bekomme ich als output für ld an dieser Stelle <__main__.MyDatatype object at 0x00B3E3F0>
@jbs : das verstehe ich noch weniger - ist es wichtig in zeile 9 zweimal foo zu bringen? ich habe das erste foo durch foe ersetzt, und es tut immernoch .. irgendwas
Danke jedenfalls für die vielen Ansätze.
Verfasst: Dienstag 16. März 2010, 19:39
von .robert
Code: Alles auswählen
In [1]: class MyDatataype(object):
...: def __init__(self, fname, x):
...: self.fname = fname
...: self.x = x
...:
...:
In [2]: ld = []
In [3]: ld.append(MyDatatype('file',5))
In [4]: ld
Out[4]: [<__main__.mdt object at 0x1233ed0>]
In [5]: ld[0]
Out[5]: <__main__.mdt object at 0x1233ed0>
In [6]: ld[0].x
Out[6]: 5
In [7]: ld[0].fname
Out[7]: 'file'
Verfasst: Dienstag 16. März 2010, 19:47
von loonquawl
Danke .robert.
das hatte ich schon so gemacht, trotzdem ist es ja schade, dass man jetzt nicht mehr ohne Zusatzaufwand den Inhalt von MyDatatype sieht, da bleibe ich lieber bei der ld[1]['x'] Nomenklatur.
Falls ich nicht der einzige Noob bin, der drüber stolpert:
http://old.nabble.com/Can%27t-create-li ... 86707.html fand ich sehr lesbar
Verfasst: Dienstag 16. März 2010, 20:21
von .robert
hä?
deine Methode:
mittels eigenem datentyp:
stimmt, ne menge Zusatzaufwand...

Verfasst: Dienstag 16. März 2010, 20:48
von loonquawl
@ .robert: nee, nicht die einzel-Abfragen, sondern das simple print ld, hatte gehofft das das klar war. Danke der Nachfrage.
Verfasst: Dienstag 16. März 2010, 20:54
von .robert
loonquawl hat geschrieben:sondern das simple print ld
Code: Alles auswählen
def __repr__(self):
return (%s, %d) % (self.fname, self.x)
Edit:
beispiel:
Code: Alles auswählen
In [14]: class MyData(object):
def __init__(self, fname, x):
self.fname = fname
self.x = x
def __repr__(self):
return self.fname
....:
....:
In [20]: ls = []
In [21]: ls.append(MyData('file',5))
In [22]: ls
Out[22]: [file]
(doku lesen wäre eine Maßnahme

)
Verfasst: Dienstag 16. März 2010, 22:30
von Darii
loonquawl hat geschrieben:Gar nicht. Was du ja wohl willst, ist eine Kopie erzeugen, und darauf dann referenzieren. Kopien einer Liste s kannst du z.B. mit s[:] erzeugen. Je nachdem was deine Liste enthaelt, muesstest du dir auch deepcopy ansehen.
... leider will ich keine Liste, sondern ein dict kopieren; s{:} funktioniert nicht, deepcopy(s) funktioniert.
deepcopy ist allerdings meistens nicht das was man will. Da auch alle Elemente kopiert werden. meist reicht ein einfaches copy.