List wird auf magische art und weise überschrieben

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
Azraelilmeraz
User
Beiträge: 6
Registriert: Samstag 27. September 2008, 14:50
Kontaktdaten:

Hallöchen leute

Ich glaube ich explodiere gleich vor Wut :).
Ich als C++ Coder bin ein Maschinennahes Programmieren gewohnt und irgendwie verwirren mich die Eigenheiten von Python immer mehr "^^

Vielleicht kennen einige von euch ja das 3D-Modelierungs Programm Blender 3D. Das Programm hat ein Scriptinginterface via Python.

Nun, ich schreibe hier grad ein Script, um mein 3D-Modell in dem gewünschten Format zu exportieren. Aber das ist eigentlich nicht so wichtig, da mein Problem wohl eher in den Grundlagen von Python liegt.

Und zwar wird eine Liste in einer Schleife mit Objekten gefüllt, und zwar so:

Code: Alles auswählen

VTris = []

print "---------------------"	
for f in me.faces:
	print "face"
	Tring = ALMTriangle2()
	for i,v in enumerate(f):
		VVerts[v.index].ca = f.col[i].a
		VVerts[v.index].cr = f.col[i].r
		VVerts[v.index].cg = f.col[i].g
		VVerts[v.index].cb = f.col[i].b
		VVerts[v.index].tu = f.uv[i].x
		VVerts[v.index].tv = f.uv[i].y
		Tring.ve[i] = VVerts[v.index]
	VTris.append(Tring)	
	indlist = [Tring.ve[2].index,\
	Tring.ve[1].index,\
	Tring.ve[0].index]
	print indlist
	
print "---------------------"
hier noch die Klassen, die ich mir da erstellt hab:

Code: Alles auswählen

class ALMVert:
	x = 0.0
	y = 0.0
	z = 0.0
	index = 0
	ca = 0
	cr = 0
	cg = 0
	cb = 0
	nx = 0.0
	ny = 0.0
	nz = 0.0
	tu = 0.0
	tv = 0.0
	def __init__(self,_x,_y,_z,_index):
		self.x = _x
		self.y = _y
		self.z = _z
		self.index = _index

class ALMTriangle:
	ve = [ALMVert(0,0,0,0),ALMVert(0,0,0,0),ALMVert(0,0,0,0)]
	def __init__(self,v1,v2,v3):
		self.ve[0] = v1
		self.ve[1] = v2
		self.ve[2] = v3
		
class ALMTriangle2(ALMTriangle):
	def __init__(self):
		pass
Wahrscheinlich ist das kein gutes Python, aber ich fahr morgen um 8 für ne Woche weg und möchte das heute noch wenigstens zum laufen kriegen, die Stilfragen kommen später :)

nun, der Code scheint an dieser Stelle bestens zu funktionieren. Ich kriege in der Blender konsole so etwas zu sehen:

face 0 :
[3,0,4]
face 1 :
[7,3,4]
face 2 :
[7,6,2]
...
face 11:
[3,2,0]

Da denk ich mir: "super, die liste ist mit richtigen Daten gefüllt, gucken wir nochmal, ob alles stimmt"

Code: Alles auswählen

for f in me.faces:
	print "face",f.index,":"
	indlist = [VTris[f.index].ve[2].index,\
	VTris[f.index].ve[1].index,\
	VTris[f.index].ve[0].index]
	print indlist
	
print "---------------------"
Ein Blick auf die Blender Konsole verpasst mir das grauen:

face 0 :
[3,2,0]
face 1 :
[3,2,0]
face 2 :
[3,2,0]
...
face 11:
[3,2,0]

Alle Daten wurden mit dem letzten Wert überschrieben. ARGH!!!
Für mich als C++ programmierer ist das absolut unlogisch. ich vermute aber, ich habe irgendein Konzept von Python nicht verstanden :/

Also die Frage: Warum fügt append() nicht ein Element hinzu, sondern überschreibt alle Elemente der Liste?
BlackJack

Das `append()` funktioniert schon. Ich würde mal darauf tippen, das Problem ist `ALMTriangle.ve`, das ist nämlich als *Klassenattribut* definiert also für alle `ALMTriangle`-Exemplare das selbe Objekt. Versuch's doch mal so:

Code: Alles auswählen

class ALMTriangle:
    def __init__(self, v1, v2, v3):
        self.ve = [v1, v2, v3]
Hier bekommt jedes `ALMTriangle`-Exemplar seine eigene, unabhängige Liste.

Wozu ist `ALMTriangle2` gut? Und die Unterstriche bei den Argumentnamen von `ALMVert.__init__()`?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

`append()` fügt schon hinzu und überschreibt nicht, allerdings benutzt du Klassenvariablen, d.h. wenn ein Exemplar der Klasse die Variable ändert, gilt das für alle Exemplare. Alle `ALMTriangle2`-Exemplare teilen sich z.B. `ve`, wodurch beim ersten print alles zu stimmen scheint, beim zweiten aber immer nur die letzten Werte gezeigt werden.

Edit: Zu lahm.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Azraelilmeraz
User
Beiträge: 6
Registriert: Samstag 27. September 2008, 14:50
Kontaktdaten:

Wow, danke. Funktioniert jetzt bestens. Muss jetzt irgendwie damit fertig werden, dass Klassenvariablen nicht extra deklariert werden müssen/dürfen :D.

Naja die Unterstriche kommen von dem C++-Denken, dass die Namen der Parametervariablen anders als die der Membervariablen sein müssen. jetzt hab ich aber verstanden, dass self.x in python was anderes als x als parameter ist. Und ALMTriangle2 ist dafür da, dass ich nicht wusste wie ich __init__() sonst überladen kann.

Jetzt siehts bei mir so aus:

Code: Alles auswählen

class ALMVert:
	def __init__(self,x,y,z,index):
		self.x = x
		self.y = y
		self.z = z
		self.index = index
		index = 0
		ca = 0
		cr = 0
		cg = 0
		cb = 0
		nx = 0.0
		ny = 0.0
		nz = 0.0
		tu = 0.0
		tv = 0.0
		
class ALMTriangle:
	def __init__(self,v1,v2,v3):
		self.ve = [v1,v2,v3]
		
class ALMTriangle2(ALMTriangle):
	def __init__(self):
		self.ve = [ALMVert(0,0,0,0),ALMVert(0,0,0,0),ALMVert(0,0,0,0)]		
Und funktioniert. Vielen Dank nochmal
BlackJack

Überladen geht nicht, weil Funktionen ja keine Typsignaturen haben, an denen man sie unterscheiden könnte. Aber man kann mit Defaultwerten arbeiten, oder Klassenmethoden für alternative Konstruktoren verwenden.

Code: Alles auswählen

class ALMTriangle(object):
     def __init__(self, v1=None, v2=None, v3=None):
         self.ve = [(v if v is not None else ALMVert(0, 0, 0, 0))
                    for v in (v1, v2, v3)]

    # Alternativ:
    @classmethod
    def zero(cls):
        return cls(ALMVert(0, 0, 0, 0),
                   ALMVert(0, 0, 0, 0),
                   ALMVert(0, 0, 0, 0))
Azraelilmeraz
User
Beiträge: 6
Registriert: Samstag 27. September 2008, 14:50
Kontaktdaten:

warum muss ALMTriangle hier von "object" erben?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Azraelilmeraz hat geschrieben:warum muss ALMTriangle hier von "object" erben?
Damit es eine new-style-Klasse wird, wie sie Python 3 durchweg verwendet.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Stichwort "New Style Classes".
BlackJack

@Azraelilmeraz: Es *muss* nicht von `object` erben, ich hab's mir aber so angewöhnt, weil `properties` nur auf "new style"-Klassen funktionieren.

Nochmal zu Deinem Quelltext: Dir ist klar, das alles ab Zeile 7 in Deiner `__init__()` ziemlich effektfrei ist, weil da nur lokale Namen gebunden werden, die nach abarbeiten der Funktion wieder verschwinden!?
Azraelilmeraz
User
Beiträge: 6
Registriert: Samstag 27. September 2008, 14:50
Kontaktdaten:

BlackJack hat geschrieben:@Azraelilmeraz: Es *muss* nicht von `object` erben, ich hab's mir aber so angewöhnt, weil `properties` nur auf "new style"-Klassen funktionieren.

Nochmal zu Deinem Quelltext: Dir ist klar, das alles ab Zeile 7 in Deiner `__init__()` ziemlich effektfrei ist, weil da nur lokale Namen gebunden werden, die nach abarbeiten der Funktion wieder verschwinden!?
Oops, hat aber komischerweise funktioniert.
Naja- Da ich für die Engine meines Spiels später ein Python-Interface einbauen will, werde ich wohl noch mehr mit Python zu tun haben.
Auf jeden fall sehen meine Klassen nun so aus:

Code: Alles auswählen

class ALMVert(object):
	def __init__(self,x,y,z,index):
		self.x = x
		self.y = y
		self.z = z
		self.index = index
		self.ca = 0
		self.cr = 0
		self.cg = 0
		self.cb = 0
		self.nx = 0.0
		self.ny = 0.0
		self.nz = 0.0
		self.tu = 0.0
		self.tv = 0.0
		
class ALMTriangle(object):
	def __init__(self,v1=None,v2=None,v3=None):
		self.ve = [(v if v is not None else ALMVert(0, 0, 0, 0)) for v in (v1, v2, v3)]
		
Wobei diese Listendefinition bei ALMTriangle für mich grad mal nach syntaxgefrickel aussieht xD. Aber ich hab ja nie behauptet ein Python-profi zu sein :)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Azraelilmeraz hat geschrieben:Oops, hat aber komischerweise funktioniert.
Natürlich funktioniert das, da du in Python ja nicht deklarieren musst sondern beliebige Attribute den Objekten zuweisen kannst. Nichts anderes machst du ja in ``__init__``.

Code: Alles auswählen

class A(object):
    pass

a = A()
a.ca = 3
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Azraelilmeraz
User
Beiträge: 6
Registriert: Samstag 27. September 2008, 14:50
Kontaktdaten:

Also Python ist schon was hübsches :D. Sieht für mich zwar nach ner Menge Aufwand interpreterseits aus, aber davon kriegt man ja nichts mit :)
Antworten