Wert wird ü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
Lynx
User
Beiträge: 4
Registriert: Samstag 23. September 2006, 22:48

Guten Abend.
Ich beschäftige mich seit 'n paar Tagen mit Python, und muss sagen, dass ich von der Sprache begeistert bin. Jetzt steh ich allerdings vor einem Problem, bzw. kann das Verhalten des Interpreten in keinster Weise nachvollziehen:

Code: Alles auswählen

class Object:
	daten = {}
	
	def __init__(self, daten):
		for wert, inhalt in daten.iteritems():
			self.daten[wert] = inhalt

class World:
	objects = {}
	
	def __init__(self):
		zu_verarbeitende_daten = {}
		zu_verarbeitende_daten['test1'] = {}
		zu_verarbeitende_daten['test1']['wert1'] = "Ich bin der Inhalt von ['test1']['wert1']"
		zu_verarbeitende_daten['test2'] = {}
		zu_verarbeitende_daten['test2']['wert1'] = "Ich bin der Inhalt von ['test2']['wert1']"
		
		for name, daten in zu_verarbeitende_daten.iteritems():
			self.addObject(name, daten)
	
	def addObject(self, name, daten):
		if not self.objects.has_key(name):
			self.objects[name] = {}
		
		self.objects[name] = Object(daten)

##### main() #####
world = World()
for name, object in world.objects.iteritems():
	for wert, inhalt in object.daten.iteritems():
		print "name = " + name + "; wert = " + wert + "; inhalt = " + inhalt
Die Ausgabe, die in der letzen Zeile erzeugt wird, sieht folgendermaßen aus:

Code: Alles auswählen

name = test1; wert = wert1; inhalt = Ich bin der Inhalt von ['test2']['wert1']
name = test2; wert = wert1; inhalt = Ich bin der Inhalt von ['test2']['wert1']
Warum ist der Inhalt der beiden Variablen auf einmal gleich?
Er hat ja - völlig korrekt - zwei Objekte erzeugt, aber ich begreife nicht, wieso die beiden Werte dieselben sind.

- Lynx
BlackJack

`objects` ist bei Dir an die *Klasse* gebunden und nicht an ein Exemplar der `World` Klasse. Wenn Du Attribute haben möchtest die in jedem Objekt unabhängig voneinander sind, dann musst Du die an die Objekte binden und nicht an die Klasse:

Code: Alles auswählen

In [3]: class World(object):
   ...:     a = list()
   ...:     def __init__(self, name):
   ...:         self.a.append(name.upper())
   ...:         self.b = list()
   ...:         self.b.append(name)
   ...:

In [4]: earth = World('Earth')

In [5]: eressea = World('Eressea')

In [6]: for world in (earth, eressea):
   ...:     print world.a, world.b
   ...:
['EARTH', 'ERESSEA'] ['Earth']
['EARTH', 'ERESSEA'] ['Eressea']
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Kleiner Exkurs:
'object' (lowercase) ist ein Typ in Python, ebenso wie etwa 'list' und 'dict'. Davon kann man auch Klassen ableiten. Deswegen wird es auch in Zeile 30 und 31 hervorgehoben, auch wenn in diesem Fall zumindest lokal der Typ durch deine eigene gleichnamige Schleifenvariable überschrieben wird.

Früher wurden Klassen in Python nur so definiert:

Code: Alles auswählen

class Person:
    pass
Mit Python 2.2 wurden die sog. 'new-style classes' eingeführt, die sich in einigen Punkten unterscheiden, und immer von etwas abgeleitet sein müssen:

Code: Alles auswählen

class Person(object):
    pass
Dabei ist 'object' in der Hierachie ganz oben, alles ist also (soweit new-style-Klassen benutzt wurden) direkt oder indirekt eine Instanz von 'object'.
/Exkurs


Dein Verständnisproblem beruht, wie BlackJack ja schrieb, darauf, dass du Klassenvariablen (in Java sind das die mit 'static') definierst, aber Member-/Instanz-/Objektvariablen willst. Zeile zwei müsstest du also um ein vorangestelltes 'self.' (in Python gilt 'explicit is better than implicit' und das 'self' drückt aus, dass es sich auf die Instanz bezieht) ergänzen und sie in den Konstruktor verschieben.

Deine letzte Zeile kannst du übrigens über die printf-Syntax (s. Python-Doku) verkürzen und (wenn man sie verstanden hat) lesbarer darstellen:

Code: Alles auswählen

print "name = " + name + "; wert = " + wert + "; inhalt = " + inhalt
# wird zu
print "name = %s; wert = %s; inhalt = %s" % (name, wert, inhalt)

P.S.: Frage an Foren-Ältere: Wie krieg ich Code-Tags dazu, den Python-Highlighter zu benutzen? Shebang-Line voranstellen (umständlich)?
BlackJack

Y0Gi hat geschrieben:P.S.: Frage an Foren-Ältere: Wie krieg ich Code-Tags dazu, den Python-Highlighter zu benutzen? Shebang-Line voranstellen (umständlich)?
Das Anfangstag so schreiben: ``code=py``. Hab's in Deinem Posting mal geändert.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Die Firma dankt.
Lynx
User
Beiträge: 4
Registriert: Samstag 23. September 2006, 22:48

Hm, gut, was lernen wir daraus? Einiges das man aus anderen Sprachen kennt lässt sich nicht so ohne weiteres auf Python übertragen. Vielen dank für die Hilfe, jetzt klappt es.

- Lynx
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Das mag generell stimmen, ist aber auch keine Überraschung, da diversen Sprachen unterschiedliche Idiome zugrunde liegen. Als Ausnahmesprache würde ich Python aber keineswegs bezeichnen.

In deinem konkreten Fall sehe ich keinen großen Unterschied zu OOP in bspw. C++ oder Java. Wo siehst du ihn konkret?

FYI: Das 'self' stammt übrigens von Smalltalk, der OO-"Muttersprache".
Lynx
User
Beiträge: 4
Registriert: Samstag 23. September 2006, 22:48

Zum einen haben wir das konsequente, explizite "self", das es in der Form weder C++ noch PHP (was die Sprachen sind, mit denen ich mich bissher am intensivsten auseinander gesetzt hab) gibt, bzw. nicht zwingend erforderlich ist.

Zum anderen war meine Idee, dass ich alle Variablen ausdrücklich direkt unter dem Kopf der Klasse initialisiere, um einen besseren Überblick zu behalten
Ich ging davon aus, das in diesem Fall ein explizites 'self.' nicht nötig ist, sondern, wenn es "static" sein soll, das in irgendeiner Weise kenntlich gemacht werden muss (worüber ich auch nicht näher nachgedacht hab, da ich z.Z. keine sinnvolle Anwendung für statische Variablen hab).

Es ist aber richtig, das es keinen Weg gibt, die Werte ausserhalb von Funktionen zu initialisieren (von Slots mal abgesehen)? Ein self.variable an der Stelle sagt mir beim erstellten des Objekts, dass es kein "self" gibt (was, wie ich annehme, daher kommt, dass self erst beim Aufruf der __init__-Methode dieser übergeben wird, und erst nach der Initialisierung der Variablen aufgerufen wird)

- Lynx

Edit: Wobei ich sagen muss, dass mir gerade die Konsequenz, die bei einigen Dingen in Python - wie eben das self - gefällt. Ist nur, wenn man vor kurzem noch mit PHP gearbeitet hat, etwas ungewöhnlich ;)
Antworten