list of dictionary, erstellen durch kopieren von Referenzen

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.
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

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.
Zuletzt geändert von loonquawl am Mittwoch 17. März 2010, 08:19, insgesamt 1-mal geändert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

loonquawl hat geschrieben:Leider ist das Verhalten sehr erratisch,
:shock:

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.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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.)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Sehr löblich, dass Du das in einer Shell ausprobierst!

Probiere mal folgendes in der Shell:

Code: Alles auswählen

ld[0] is ld[1]
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

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?
Zuletzt geändert von loonquawl am Mittwoch 17. März 2010, 08:20, insgesamt 1-mal geändert.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

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.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

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 ...
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Wenn du x-Dictionaries hast, die immer die gleichen Keys haben, wäre eine eigener Datentyp in Form einer Klasse deutlich eleganter und lesbarer.

Code: Alles auswählen

ld[0]['x']
#vs.
ld[0].x
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

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....
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

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 ;)
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

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...
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Code: Alles auswählen

class MyDatatype(object):
    def __init__(self, fname, x):
        self.fname = fname
        self.x = x
ld.append(MyDatatype('file', 5))
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

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?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

@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.
.robert
User
Beiträge: 274
Registriert: Mittwoch 25. April 2007, 17:59

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'
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

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
.robert
User
Beiträge: 274
Registriert: Mittwoch 25. April 2007, 17:59

hä?

deine Methode:

Code: Alles auswählen

ld[1]['x']
mittels eigenem datentyp:

Code: Alles auswählen

ld[1].x
stimmt, ne menge Zusatzaufwand...
:roll:
loonquawl
User
Beiträge: 8
Registriert: Dienstag 16. März 2010, 16:19

@ .robert: nee, nicht die einzel-Abfragen, sondern das simple print ld, hatte gehofft das das klar war. Danke der Nachfrage.
.robert
User
Beiträge: 274
Registriert: Mittwoch 25. April 2007, 17:59

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 :wink: )
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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