Datenweitergabe zwischen Instanzen durch Vererbung?

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.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

Hi mutetella,
das sind gute Vorschläge aber wie setzte ich so etwas um?
Ich habe es erfolglos mit getter und setter des property objects probiert ( http://docs.python.org/2/library/functi ... l#property )

Code: Alles auswählen

class Feder(object):
    def __init__(self, F):
        self.F = F
        self.steifigkeit = 100.0        
    @property
    def deltaX(self):
        return self.F/self.steifigkeit
  
class Auto(object):
    def __init__(self):            
        self._M = 2000.0
        self.g = 9.81    
        self.feder1 = Feder(self.F)
    @property
    def M(self):
        return self._M        
    @M.setter
    def M(self,value):
        self._M = value            
    @property
    def F(self):
        return self.M*self.g
        
a = Auto()
print a.feder1.deltaX           # 196.2
a.feder1.steifigkeit = 200.0
print a.feder1.deltaX           #   98.1
a.M = 1000.0
print a.feder1.deltaX           # 98.1
Leider klappt das ganze nicht bei der Feder. Könntest du mir da noch mal auf die Sprünge helfen?
BlackJack

@schneitzmaster: `Auto.M` als `property()` ist hier irgendwie sinnfrei. Wenn man in den Methoden nichts mit dem Wert macht ausser ihn einem Attribut zuzuweisen beziehungsweise abzufragen, hat man doch vom Effekt keinen Unterschied zu einem ganz normalen Attribut.

Du müsstest der `Feder` wohl statt dem `F`-Wert das `Auto`-Exemplar mitgeben und bei `deltaX()` dann denn *aktuellen* `F`-Wert vom Auto abfragen.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

Ja genau und wie kann ich das Auto-Exemplar mitgeben? Das habe ich ja die ganze Zeit in den Posts vorher mit der Vererbung versucht. Klappt aber nicht Vererbung ja etwas komplett anderes ist.
Welcher Befehl oder welches Python-spezifische Schlüsselwort, über das ich mich im Netz informieren kann, gibt es da?
BlackJack

@schneitzmaster: Dazu brauchst Du weder einen Befehl noch ein Python-spezifisches Schlüsselwort. Du musst einfach nur das `Auto`-Exemplar übergeben. Also statt dem `F`-Attribut vom `Auto`-Exemplar das `Auto`-Exemplar *selbst*. Falls das jetzt als Info nicht reicht, solltest Du den letzten Satz mal auf Englisch übersetzen. Und/oder Dir überlegen welche Namen Dir in der `Auto.__init__()` lokal zur Verfügung stehen (Spoiler: es ist ja nur einer) und welchen Typ die darab gebundenen Objekte haben.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

Okay jetzt hab ich's gerafft.

Code: Alles auswählen

lass Feder(object):
    def __init__(self, auto):
        self.auto = auto
        self.steifigkeit = 100.0        
    @property
    def deltaX(self):
        _F = self.auto.F        
        return _F/self.steifigkeit
  
class Auto(object):
    def __init__(self):            
        self.M = 2000.0
        self.g = 9.81    
        self.feder1 = Feder(self)       
    @property
    def F(self):
        return self.M*self.g
        
a = Auto()
print a.feder1.deltaX           # 196.2
a.feder1.steifigkeit = 200.0
print a.feder1.deltaX           #   98.1
a.M = 1000.0
print a.feder1.deltaX
Aber könnte man das nicht irgendwie eleganter lösen mit der Methode die mutetella vorgeschlagen. Denn so muss ich mir ja lokal in "Feder" erst alle Variablen aus auto neu definieren oder über einen länglichen Befehel (self.auto.F) darauf zugreifen, so dass komplizierte Formeln schnell unlesbar werden.
Leider weiß ich nicht genau wie er das mit den Methoden zum setzen und ändern gemeint hat.
BlackJack

@schneitzmaster: Ein führender Unterstrich bei lokalen Namen wird von einigen Programmierern als Kennzeichnung von Namen die nicht verwendet werden benutzt. Warum hast Du den in `Feder.deltaX()` verwendet? Den Namen hätte ich wahrscheinlich sowieso weg gelassen.

Der „längliche Befehl” ist eigentlich normal. Normal wäre auch `erdbeschleunigung` statt `g`, `masse` statt `M`, und `kraft` statt `F` zu verwenden.

Je nachdem wie das konkrete Problem aussieht welches man lösen möchte könnte man `Feder.auto` auch allgemeiner benennen. `Feder.last` oder so.

Die 1 bei `feder1` sollte da auch weg. Wenn man anfängt Namen mit Nummern zu ergänzen, will man in 99% der Fälle eigentlich die Werte in eine Datenstruktur stecken. Zum Beispiel in eine Liste.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@schneitzmaster
Ich verstehe nicht wirklich, was Du mit 'eleganter' meinst. Deine Lösung macht doch das, was sie soll, oder was erwartest Du anderes?

Ich hab' Deinen Code insofern angepasst, dass ein Auto mehrere Federn besitzen kann um zu demonstrieren, dass sich eine Änderung auf die Masse auch auf eine erneute Berechnung von 'delta_X' auswirkt ohne dies explizit den Federn mitzuteilen:

Code: Alles auswählen

class Damper(object):
    def __init__(self, number, stiffness, car):
        self.number = number
        self.stiffness = stiffness
        self.car = car

    @property
    def delta_X(self):
        return self.car.force / self.stiffness

class Car(object):
    gravity = 9.81
    def __init__(self, mass, *dumpers):
        self.mass = mass
        self.dampers = [
            Damper(number, stiffness, self) for number, stiffness in dumpers]

    @property
    def force(self):
        return self.mass * self.gravity

    def get_dampers(self):
        return [(damper.number, damper.delta_X) for damper in self.dampers]

Code: Alles auswählen

>>> c = Car(2000.0, ('1', 100.0), ('2', 100.0), ('3', 200.0), ('4', 200.0))
>>> c.get_dampers()
[('1', 196.19999999999999), ('2', 196.19999999999999), ('3', 98.099999999999994), ('4', 98.099999999999994)]
>>> c.mass = 1000.0
>>> c.get_dampers()
[('1', 98.099999999999994), ('2', 98.099999999999994), ('3', 49.049999999999997), ('4', 49.049999999999997)]
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

schneitzmaster hat geschrieben:Leider weiß ich nicht genau wie er das mit den Methoden zum setzen und ändern gemeint hat.
Damit meinte ich, dass Du Klassen über deren Methoden verbinden kannst. Letztlich ist das eine Detailfrage. Ich persönlich mag diese property-Geschichten nicht besonders. Wenn ich ein Attribut abfrage, möchte ich ein Attribut abfragen und nicht eine Methode dahinter aufrufen. Ok, oft drängt sich einem ein property förmlich auf (z. B. bei Koordinaten) und manchmal mag es auch Sinn machen... Ist halt auch geschmackssache.
Zudem gefällt es mir besser, wenn immer nur so viel an Information weitergegeben wird, wie auch wirklich notwendig ist. In Deinem Fall finde ich, dass eine Feder nicht alle Informationen des Autos benötigt. Eine Feder muss noch nicht einmal wissen, dass sie in ein Auto verbaut ist. Die gerade gezeigt Lösung könnte also auch so aussehen:

Code: Alles auswählen

class Damper(object):
    def __init__(self, number, stiffness, load):
        self.number = number
        self.stiffness = stiffness
        self.load = load

    @property
    def delta_X(self):
        return self.load() / self.stiffness

class Car(object):
    gravity = 9.81
    def __init__(self, mass, *dumpers):
        self.mass = mass
        self.dampers = [
            Damper(number, stiffness, self.force) for 
            number, stiffness in dumpers]

    def force(self):
        return self.mass * self.gravity

    def get_dampers(self):
        return [(damper.number, damper.delta_X) for damper in self.dampers]
Das macht, wie BlackJack schon erwähnt hat, die 'Damper'-Klasse auch unabhängiger.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

@BlackJack: Ich habe den Unterstrich verwendet um klar zu machen, dass diese Variable nicht von Feder exportiert werden soll, sondern nur lokal in der Methode zugewiesen wird um die danach folgende Rechnung "Übersichtlicher zu machen". Im Python-Wiki habe ich diese Notation gefunden (http://de.wikibooks.org/wiki/Python_unt ... und_um_OOP --> "Privat - mehr oder weniger").
Bei meinem tatsächlichen Problem sind die Formeln sehr länglich und da wird es schnell unübersichtlich wenn jedesmal mit dem Punktoperatur darauf zugreift. Deswegen finde ich es an der stelle praktischer eine lokale Variable einzuführen.

@mutetella: Tatsächlich ist es so, dass ich ein differentialgeometrisches Problem programmiere, wo die Koordinaten des "Master"-Körpers in der Masterklasse berechnet werden und sich dann daraus die Koordinaten der "Slave"-Körper rechnerisch in der Slaveklasse bestimmen. Ich glaube dass da property, zu mindest für mich, der beste und einfachste weg ist.

Ich habe jetzt nur noch eine letzte Frage. Warum ist in deinem Code bei der Definition von delta_X load() eine Methode obwohl doch load in der Klasse Damper als Attribut initialisiert wird?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

schneitzmaster hat geschrieben:... obwohl doch load in der Klasse Damper als Attribut initialisiert wird?
Ein Attribut ist letztlich nichts anderes, als der Verweis über einen Namen auf ein Objekt. Ob dieses Objekt eine Liste, ein String, eine Funktion/Methode oder sonstwas ist, spielt keine Rolle. Nachdem Du ein Klassenattribut erstellt hast kannst Du über den Namen des Attributs auf das verwiesene Objekt und all seine Eigenschaften zugreifen.
In meinem Beispiel übergebe ich beim Erstellen der 'Damper'-Exemplare unter anderem die Methode 'Car.force', die innerhalb der 'Damper.__init__'-Methode an das Klassenattribut 'Damper.load' gebunden wird. Damit verhält sich 'Damper.load' wie die Methode 'Car.force', weil eben (der Name) 'Damper.load' nichts anderes ist als ein Link zu 'Car.force'.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@schneitzmaster: Das lokale Namen nicht exportiert werden ist implizit klar, denn es sind ja lokale Namen. Da muss man nichts kennzeichnen und ich habe dazu auch nichts in dem Wikibook-Text gefunden. Dort geht es um Attribute von Objekten und nicht um lokale Namen.

@mutetella: Statt Klassenattribut meinst Du IMHO jeweils Instanzattribut. Jetzt mal davon abgesehen dass es zwei „Arten” von Methoden gibt — ungebundene auf der Klasse tatsächlich als Klassenattribut, und gebundene Methoden auf Exemplaren/Instanzen.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

@BlackJack:
Ja es ist klar das lokale Variablen nicht exportiert werden ist klar. Dennoch ist auf der Wiki-Seite unter der Überschrift "Privat - mehr oder weniger" als erster Satz folgendes zu finden:
Um gegenüber den Nutzern einer Klasse anzudeuten, dass bestimmte Methoden oder Attribute privat sind, also nicht nach außen exportiert werden sollen, stellt man ihnen einen Unterstrich (_) vorran
Ich interpretiere das so, dass man vor Variablen/Attributen die nicht exportiert werden einen unterstrich setzt.
Aber vielleicht vermische ich gerade Attribute und Variablen (oh man, ich weiß schon warum ich kein Informatik studiert habe).
so long...

P.S.: Mein Code funktioniert jetzt so wie er soll und das hauptsächlich durch eure Hilfe!
Danke dafür noch mal an alle die sich hier so rege beteiligt haben.
BlackJack

@schneitzmaster: Attribute sind die Werte auf die man mit dem Punkt-Operator oder `getattr()` zugreift. Variable ist ein recht schwammiger Begriff, den man manchmal auch für Attribute verwendet und für Namen auf Modulebene (Attribute des Modul-Objekts) und lokale Namen.
Antworten