Erstellen einer Liste von Objekten in Python

83

Ich versuche, ein Python-Skript zu erstellen, das mehrere Datenbanken öffnet und deren Inhalt vergleicht. Beim Erstellen dieses Skripts bin ich auf ein Problem beim Erstellen einer Liste gestoßen, deren Inhalt Objekte sind, die ich erstellt habe.

Ich habe das Programm für diesen Beitrag auf den Punkt gebracht. Zuerst erstelle ich eine neue Klasse, erstelle eine neue Instanz davon, weise ihr ein Attribut zu und schreibe sie dann in eine Liste. Dann weise ich der Instanz einen neuen Wert zu und schreibe ihn erneut in eine Liste ... und immer wieder ...

Das Problem ist, dass es immer dasselbe Objekt ist, also ändere ich wirklich nur das Basisobjekt. Wenn ich die Liste lese, erhalte ich immer wieder die Wiederholung desselben Objekts.

Wie schreibt man Objekte in eine Liste innerhalb einer Schleife?

Hier ist mein vereinfachter Code

class SimpleClass(object):
    pass

x = SimpleClass
# Then create an empty list
simpleList = []
#Then loop through from 0 to 3 adding an attribute to the instance 'x' of SimpleClass
for count in range(0,4):       
    # each iteration creates a slightly different attribute value, and then prints it to
# prove that step is working
# but the problem is, I'm always updating a reference to 'x' and what I want to add to
# simplelist is a new instance of x that contains the updated attribute

x.attr1= '*Bob* '* count
print "Loop Count: %s Attribute Value %s" % (count, x.attr1)
simpleList.append(x)

print '-'*20
# And here I print out each instance of the object stored in the list 'simpleList'
# and the problem surfaces.  Every element of 'simpleList' contains the same      attribute value

y = SimpleClass
print "Reading the attributes from the objects in the list"
for count in range(0,4):
    y = simpleList[count]
    print y.attr1

Wie füge ich die Elemente von simpleList hinzu (anhängen, erweitern, kopieren oder was auch immer), sodass jeder Eintrag eine andere Instanz des Objekts enthält, anstatt dass alle auf dieselbe verweisen?

anon
quelle
2
Ich schlage vor, Sie verwenden Iteratoren anstelle von Zählern: "für Element in simpleList:" sieht viel besser aus.
Mapad

Antworten:

68

Sie zeigen ein grundlegendes Missverständnis.

Sie haben nie eine Instanz von SimpleClass erstellt, weil Sie sie nicht aufgerufen haben.

for count in xrange(4):
    x = SimpleClass()
    x.attr = count
    simplelist.append(x)

Wenn Sie die Klasse stattdessen Parameter verwenden lassen, können Sie ein Listenverständnis verwenden.

simplelist = [SimpleClass(count) for count in xrange(4)]
Eisenfrosch
quelle
2
Nicht verfügbar für Python 3.x - xrange () func wird nicht mehr unterstützt.
r3t40
3
@TitPoplatnik Ersetzen Sie einfach xrange () durch range ().
EXPRESS
@ironfroggy ist es möglich, den Index, in dem das Objekt gespeichert ist, aus der Klasse abzurufen. Angenommen, Klasse A befindet sich in einer Liste an Position 3 [x, x, x, A, x].
Ist
54

Um eine Liste mit separaten Instanzen einer Klasse zu füllen, können Sie in der Deklaration der Liste eine for-Schleife verwenden. Das * Multiplizieren verknüpft jede Kopie mit derselben Instanz.

instancelist = [ MyClass() for i in range(29)]

und greifen Sie dann über den Index der Liste auf die Instanzen zu.

instancelist[5].attr1 = 'whamma'
Zoomulator
quelle
3
besser sein: instancelist = [MyClass () für _ in range (29)]
tutormike
10

Es sollte nicht erforderlich sein, das SimpleClass-Objekt jedes Mal neu zu erstellen, wie einige vorschlagen, wenn Sie es einfach zum Ausgeben von Daten basierend auf seinen Attributen verwenden. Sie erstellen jedoch keine Instanz der Klasse. Sie erstellen einfach einen Verweis auf das Klassenobjekt selbst. Daher fügen Sie der Liste immer wieder einen Verweis auf dasselbe Klassenattribut hinzu (anstelle des Instanzattributs).

Anstatt:

x = SimpleClass

du brauchst:

x = SimpleClass()
Daniel Naab
quelle
7
x = SimpleClass() erstellt das Objekt jedes Mal neu.
neil
5

Erstellen Sie jedes Mal eine neue Instanz, wobei jede neue Instanz den richtigen Status hat, anstatt den Status derselben Instanz kontinuierlich zu ändern.

Speichern Sie alternativ bei jedem Schritt eine explizit erstellte Kopie des Objekts (unter Verwendung des Hinweises auf dieser Seite ) anstelle des Originals.

joel.neely
quelle
3

Wenn ich Ihre Frage richtig verstehe, fragen Sie nach einer Möglichkeit, eine tiefe Kopie eines Objekts auszuführen. Was ist mit copy.deepcopy?

import copy

x = SimpleClass()

for count in range(0,4):
  y = copy.deepcopy(x)
  (...)
  y.attr1= '*Bob* '* count

Eine Deepcopy ist eine rekursive Kopie des gesamten Objekts. Weitere Informationen finden Sie in der Python-Dokumentation: https://docs.python.org/2/library/copy.html

Roberto Liffredo
quelle
2
Die Wiederverwendung eines Objekts - Deep Copy oder Clone - ist eine häufige Verwirrung bei n00b. Manchmal kann man die Frage, die zu stellen scheint, nicht beantworten. Manchmal muss man die Frage beantworten, die sie hätten stellen sollen.
S.Lott
3

Ich denke, dies zeigt einfach, was Sie erreichen wollen:

# coding: utf-8

class Class():
    count = 0
    names = []

    def __init__(self,name):
        self.number = Class.count
        self.name = name
        Class.count += 1
        Class.names.append(name)

l=[]
l.append(Class("uno"))
l.append(Class("duo"))
print l
print l[0].number, l[0].name
print l[1].number, l[1].name
print Class.count, Class.names

Führen Sie den obigen Code aus und Sie erhalten: -

[<__main__.Class instance at 0x6311b2c>, 
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']
Triple Decker
quelle