Ich möchte gerne in die Objektorientierung einsteigen. Leider gibt mein Buch "Python 3 - Lernen und professionell anwenden" keine mir verständliche Einführung in das objektorientierte Programmieren.
Deshalb wollte ich fragen, ob ihr kostenlose Ressourcen kennt, mit denen man in dieses Thema Stück für Stück einsteigen kann. Gut wäre es, wenn auch Übungsaufgaben oder Beispielprojekte enthalten wären.
Einstieg in die Objektorientierung
Ich kann mich gut daran erinnern, dass das für mich auch ein Problem war. Ich habe es dann seinerzeit durch eine Anleitung für php verstanden, die ich lesen musste. Schau mal, ob du nachfolgendes Beispiel in Python verstehst:
Wenn ich gefragt würde: Um Objektorientierung wird zunächst zu viel Wind gemacht. Das Konzept an sich ist sehr einfach und hat enorme Vorteile, wenn man es versteht und verwenden kann. Aber das heißt eben nicht, dass Objektorientierung an sich komplex ist. Meine Erfahrung ist, dass die gängigen Einführungen Objektorientierung an möglichst komplexen Beispielen erklären, vielleicht um die Möglichkeiten des Konzepts aufzuzeigen, was aber Einsteigern nicht hilft.
Versuch mal Beispiele aus deinem Umfeld, womit du dich typischerweise beschäftigst entsprechend umzusetzen. Das ist es dann eben. Im Kern nutze ich init- sowie die set- und get-Methoden und packe dorthin meine Programmlogik. Versuch mal eine Spielerfigur eines Spiels zu implementieren. Die hat dann Lebenspunkte, Hitpunkte und Schildpunkte und kann zuschlagen und einstecken und durch Zaubertränke kann man die Lebenspunkte oder die Hit- oder Schildpunkte erhöhen und dann kann man halt noch fragen, ob sie noch lebt, wie der Name ist und welcher Gilde sie angehört o. ä. So etwas kann man leicht mittels Objektorientierter Programmierung umsetzen.
Nur am Rande: Obiges Beispiel ist wahrscheinlich nicht gut und nicht flüssig, aber bis jemand von den Experten antwortet, ist es vielleicht illustrierend.
Code: Alles auswählen
class Testobjekt(object):
name = "Rechteck"
def __init__(self, breite, hoehe):
self.breite = breite
self.hoehe = hoehe
if breite == hoehe:
self.name = "Quadrat"
else:
self.name = "Rechteck"
def set_breite(self, breite):
self.breite = breite
if self.breite == self.hoehe:
self.name = "Quadrat"
else:
self.name = "Rechteck"
def set_hoehe(self, hoehe):
self.hoehe = hoehe
if self.breite == self.hoehe:
self.name = "Quadrat"
else:
self.name = "Rechteck"
def get_breite(self):
return self.breite
def get_flaeche(self):
return self.breite * self.hoehe
def get_name(self):
return self.name
if __name__ == "__main__":
t = Testobjekt(12, 2)
print("Es handelt sich um ein {}.".format(t.get_name()))
print("Die Fläche beträgt {} cm².".format(t.get_flaeche()))
t.set_hoehe(12)
print("Es handelt sich um ein {}.".format(t.get_name()))
print("Die Fläche beträgt {} cm².".format(t.get_flaeche()))
Versuch mal Beispiele aus deinem Umfeld, womit du dich typischerweise beschäftigst entsprechend umzusetzen. Das ist es dann eben. Im Kern nutze ich init- sowie die set- und get-Methoden und packe dorthin meine Programmlogik. Versuch mal eine Spielerfigur eines Spiels zu implementieren. Die hat dann Lebenspunkte, Hitpunkte und Schildpunkte und kann zuschlagen und einstecken und durch Zaubertränke kann man die Lebenspunkte oder die Hit- oder Schildpunkte erhöhen und dann kann man halt noch fragen, ob sie noch lebt, wie der Name ist und welcher Gilde sie angehört o. ä. So etwas kann man leicht mittels Objektorientierter Programmierung umsetzen.
Nur am Rande: Obiges Beispiel ist wahrscheinlich nicht gut und nicht flüssig, aber bis jemand von den Experten antwortet, ist es vielleicht illustrierend.
@pixewakb: Also für Python ist das Beispiel wirklich nicht gut, denn da würde man keine Getter/Setter schreiben sondern Properties verwenden. Und zwar für `name`, denn die Codewiederholung in beiden Settern ist unschön, das würde man auch mit Gettern und Settern nicht so machen.
Das Klassenattribut `name` wird nicht benutzt und gehört da auch nicht hin.
Edit: Das Beispiel in „pythonisch“:
Das Klassenattribut `name` wird nicht benutzt und gehört da auch nicht hin.
Edit: Das Beispiel in „pythonisch“:
Code: Alles auswählen
class Form(object):
def __init__(self, breite, hoehe):
self.breite = breite
self.hoehe = hoehe
@property
def name(self):
return 'Quadrat' if self.breite == self.hoehe else 'Rechteck'
@property
def flaeche(self):
return self.breite * self.hoehe
def main():
form = Form(12, 2)
print('Es handelt sich um ein {0.name}.'.format(form))
print('Die Fläche beträgt {0.flaeche} cm².'.format(form))
form.hoehe = 12
print('Es handelt sich um ein {0.name}.'.format(form))
print('Die Fläche beträgt {0.flaeche} cm².'.format(form))
if __name__ == '__main__':
main()
Objektorientierte Programmierung, wie eigentlich irgendeine Programmierung, lernt man am besten, wenn man selbst ein Problem lösen will. Dann stellt man sich erst die Frage, wie kann ich das Problem lösen und als nächstes dann, wie kann ich es besser lösen. In Python ist alles ein Objekt. Leider sind viele Beispiele einfach nur Java-Beispiele nach Python umgeschrieben. Und viele Konzepte und Pattern, die man in Java braucht, sind in Python nur überflüssig. Z.B. set_xxx und get_xxx-Methoden. Falls man das tatsächlich braucht, schreibt man das als Properties
Und dann sieht man, dass eigentlich name dann berechnen kann, wenn man es braucht und vereinfacht das zu:
Code: Alles auswählen
class Testobjekt(object):
def __init__(self, breite, hoehe):
self._breite = breite
self._hoehe = hoehe
self.name = self._determine_name()
def _determine_name(self):
return "Quadrat" if self.breite == self.hoehe else "Rechteck"
@property
def breite(self):
return self._breite
@breite.setter
def breite(self, breite):
self._breite = breite
self.name = self._determine_name()
@property
def hoehe(self):
return self._hoehe
@hoehe.setter
def hoehe(self, hoehe):
self._hoehe = hoehe
self.name = self._determine_name()
@property
def flaeche(self):
return self.breite * self.hoehe
def main():
t = Testobjekt(12, 2)
print("Es handelt sich um ein {0.name}.".format(t))
print("Die Fläche beträgt {0.flaeche} cm².".format(t))
t.hoehe = 12
print("Es handelt sich um ein {0.name}.".format(t))
print("Die Fläche beträgt {0.flaeche} cm².".format(t))
if __name__ == "__main__":
main()
Code: Alles auswählen
class Testobjekt(object):
def __init__(self, breite, hoehe):
self.breite = breite
self.hoehe = hoehe
@property
def name(self):
return "Quadrat" if self.breite == self.hoehe else "Rechteck"
@property
def flaeche(self):
return self.breite * self.hoehe
- pillmuncher
- User
- Beiträge: 1527
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
Ein Quadrat ist idR aus objektorientierter Sicht kein Rechteck, weil sie unzterschiedliche Schnittstellen haben. Wenn ich bei einem Rechteck die Breite ändere, erwarte ich, dass die Höhe unverändert bleibt. Bei deiner Quadrat-Implementierung ist das anders. Das widerspricht dem Liskov-Substitutions-Prinzip.
Man kann sich aber objektorientiert ein Quadrat aus einem Rechteck bauen:
Man kann sich aber objektorientiert ein Quadrat aus einem Rechteck bauen:
Code: Alles auswählen
class Rectangle:
def __init__(self, left, top, width, height):
self.left = left
self.top = top
self.width = width
self.height = height
class Square:
def __init__(self, left, top, size):
self._rect = Rectangle(left, top, size, size)
@property
def left(self):
return self._rect.left
@left.setter
def left(self, value):
self._rect.left = value
@property
def top(self):
return self._rect.top
@top.setter
def top(self, value):
self._rect.top = value
@property
def size(self):
return self._rect.width
@size.setter
def size(self, value):
self._rect.width = value
self._rect.height = value
Zuletzt geändert von pillmuncher am Sonntag 20. September 2015, 17:36, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
@pillmuncher: so ganz passt das mit der Komposition aber nicht, ein Quadrat hat kein Rechteck sondern ist ein Rechteck:
Code: Alles auswählen
class Rectangle:
def __init__(self, left, top, width, height):
self.left = left
self.top = top
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, left, top, size):
super(Square, self).__init__(left, top, size, size)
@property
def height(self):
return self.width
@height.setter
def height(self, value):
self.width = value
Was genau kann man jetzt Xfd7887a antworten?
PS Danke fürs Feedback! Wahrscheinlich komme ich auf längere Sicht erst einmal noch nicht um set- und get-Methoden herum. (Weil mein Lehrbuch das auch verwendet hatte und es für mich im Moment noch verständlich ist, was dort passieren soll.) Als "Lernender" darf ich mal anmerken, dass das mit set und get schon anspruchsvoll ist. Die Sache mit properties dürfte für Xfd7887a und war für mich anfangs deutlich zu hoch. Ich nutze übrigens properties jetzt so langsam im Zuge des Code Refactorings.
PS Danke fürs Feedback! Wahrscheinlich komme ich auf längere Sicht erst einmal noch nicht um set- und get-Methoden herum. (Weil mein Lehrbuch das auch verwendet hatte und es für mich im Moment noch verständlich ist, was dort passieren soll.) Als "Lernender" darf ich mal anmerken, dass das mit set und get schon anspruchsvoll ist. Die Sache mit properties dürfte für Xfd7887a und war für mich anfangs deutlich zu hoch. Ich nutze übrigens properties jetzt so langsam im Zuge des Code Refactorings.
- pillmuncher
- User
- Beiträge: 1527
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
Dann müsste das ja funktionieren:Sirius3 hat geschrieben:@pillmuncher: so ganz passt das mit der Komposition aber nicht, ein Quadrat hat kein Rechteck sondern ist ein Rechteck
Code: Alles auswählen
def increase_width_but_not_height_of_rectangle(rect):
old_width = rect.width
old_height = rect.height
rect.width *= 2
assert rect.width == 2 * old_width
assert rect.height == old_height
Code: Alles auswählen
>>> increase_width_but_not_height_of_rectangle(s)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in increase_width_but_not_height_of_rectangle
AssertionError
In specifications, Murphy's Law supersedes Ohm's.
- pillmuncher
- User
- Beiträge: 1527
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@Sirius3: Wenn die Rechtwinkeligkeit der Ecken das Kriterium der Rechteckigkeit wäre, dann wäre auch das hier ein Rechteck:
Oder anders gesagt: man muss vorher definieren, was als Rechteck und Quadrat gelten soll, bevor man festlegt, was Basisklasse und was abgeleitete KLasse sein soll, oder ob so eine Beziehung überhaupt besteht. Und diese Entscheidung muss man auf Basis davon treffen, was man mit den Objekten später anfangen will, nicht davon, ob es in Platons Ideenhimmel in metaphysisch absolutem Sinne wahr ist.

Oder anders gesagt: man muss vorher definieren, was als Rechteck und Quadrat gelten soll, bevor man festlegt, was Basisklasse und was abgeleitete KLasse sein soll, oder ob so eine Beziehung überhaupt besteht. Und diese Entscheidung muss man auf Basis davon treffen, was man mit den Objekten später anfangen will, nicht davon, ob es in Platons Ideenhimmel in metaphysisch absolutem Sinne wahr ist.
In specifications, Murphy's Law supersedes Ohm's.
@Xfd7887a
Ich kenne leider keine verständliche Einführung in die objektorientierte Programmierung mit Python, aber seinerzeit hatte mir die Einleitung von Peter Kropff - allerdings für php - sehr geholfen.
OOP PHP5
Versuch nicht zwingend den php-Quellcode zu verstehen, aber lies die erläuternden Texte und versuch mal zu verstehen, was dort gemacht wird. Er schreibt *flott* und erhellend. Wenn Du das grob verstehst, dann kannst du möglicherweise mal eigene Gehversuche in Python unternehmen. Wenn das im Kern läuft, also etwa Objekt Hund, Methode trinken, Attribut durstig = True oder False usw., dann kannst du darauf aufbauend komplexere Sachen angehen.
Der Vollständigkeit halber Python Doc auf Deutsch (3.3), allerdings nicht unbedingt das, was ich als Anfänger suchen würde.
Ich kenne leider keine verständliche Einführung in die objektorientierte Programmierung mit Python, aber seinerzeit hatte mir die Einleitung von Peter Kropff - allerdings für php - sehr geholfen.
OOP PHP5
Versuch nicht zwingend den php-Quellcode zu verstehen, aber lies die erläuternden Texte und versuch mal zu verstehen, was dort gemacht wird. Er schreibt *flott* und erhellend. Wenn Du das grob verstehst, dann kannst du möglicherweise mal eigene Gehversuche in Python unternehmen. Wenn das im Kern läuft, also etwa Objekt Hund, Methode trinken, Attribut durstig = True oder False usw., dann kannst du darauf aufbauend komplexere Sachen angehen.
Der Vollständigkeit halber Python Doc auf Deutsch (3.3), allerdings nicht unbedingt das, was ich als Anfänger suchen würde.
@pixewakb: Den Text finde ich nicht wirklich gut. Der ist für PHP, vieles davon ist in Python nicht machbar/abbildbar und das was geht ist nicht wirklich „pythonisch“ *und* die OOP-Beispiele sind teilweise grottenschlecht — und zwar unabhängig von der Programmierpsrache. Was man dort lernt ist die PHP-Syntax und -Besonderheiten für Klassen aber gar nicht wie man OOP nun eigentlich einsetzt beziehungsweise werden dort Sachen gezeigt die schlechtes OOP sind.
Man kommt mit Funktionen in Python weiter als in PHP, darum kann ich das was er über strukturierte Programmierung mit Funktionen sagt nicht 100%ig unterschreiben. Andererseits treffen fast alle Punkte die er über Klassen sagt auch auf Funktionen zu:
* Sie sind so konzipiert das man die innere Logik nicht kennen muss.
* Modifikationen innerhalb der Funktionen betreffen nicht den übrigen Code.
* Funktionen werden so entwickelt das sie jederzeit wiederverwendet werden können. Also so weit das möglich oder sinnvoll ist, das gilt aber auch für Klassen. Dieses pauschale Heilsversprechen von OOP das man dann alles irgendwie wiedervernden kann stimmt letztlich ja auch nicht.
Bei den letzten beiden Punkten stimmt der erste dann halt nicht mehr für Funktionen: Man kann sie nicht durch Vererbung erweitern.
Den letzten Punkt verstehen ich nicht: Klassen enthalten bereits eine Fehlerroutine? Häh?
Für Python-Programmierer ist es etwas zu PHP-syntaxlastig (man benutzt ``->`` aber braucht kein ``$``, der Gültigkeitsoperator ``::``, …).
Das Briefbeispiel ist zum schreiend wegrennen. Eine Klasse `BriefZustellen` mit den Attributen `adresse`, `inhalt`, und `versendet` und den Methoden `werfBriefEin()`, `sortierBriefEin()`, und `stellBriefZu()`? Argh.
Insgesamt gibt's da auch viel über ``private``, ``protected``, ``public`` was so nicht auf Python übertragbar ist. Inklusive der Getter-/Setter-Methoden die man in Python nicht schreiben würde.
Und es gibt die Klasse `Mensch` mit den Methoden `getName()` und `setName()` und davon abgeleitet `Frau` mit den Methdoden `setzNamen()` und `holeNamen()`, also *andere* Namen! WTF!?
Die Klasse `Sex` ist aus objektorientierter Sicht vollkommen sinnlos. Und wenn man Typprüfungen vornimmt ist das auch fast immer ein Zeichen das man OOP nicht so anwendet wie es vorgesehen ist. Die Frage ist nämlich an der Stelle warum es Klassen für `Mann` und `Frau` gibt und warum das Geschlecht nicht einfach eine Eigenschaft ist.
Bei den magischen Methoden ist fast alles nicht auf Python anwendbar.
Eine Klasse = eine Datei sollte man sich in Python dann ganz schnell wieder abgewöhnen.
Der Hinweis bei dem `Getraenk`-Beispiel: „Im Sinne der objektorientierten Programmierung ist das Beispiel ziemlich unglücklich“ aber „Es geht nur um das Verständnis.“ ist ja echt drollig. Was soll man denn da verstehen? Wie man es *nicht* macht? Dazu müsste dann doch aber dabei stehen warum man das nicht macht und wie man es machen würde, und dann bräuchte man das furchtbar schlechte Beispiel nicht. Keine Ahnung wofür man in PHP statische Methoden verwendet, aber in anderen Sprachen üblicherweise als alternative Konstruktoren. Insbesondere bei Sprachen bei denen es keine Überladung von Methoden gibt die anhand von Typsignaturen unterschieden werden können.
Schnittstellen lässt sich wieder nicht auf Python übertragen und abstrakte Klassen auch nicht so wirklich.
Iteratoren sind interessant, aber die Schnittstelle ist in PHP anders und deutlich umständlicher als in Python. Ausserdem ist das Beispiel schlecht gewählt. Da sollte man eher einen typischen Containertyp zeigen und keine Frau über deren Handtascheninhalt iteriert wird.
Ach so: Insgesamt ist der Text nichts für Frauen/Mädchen, es sei denn sie möchten gerne als Objekte betrachtet werden. Klasse hat der Text dort nicht wirklich…
Man kommt mit Funktionen in Python weiter als in PHP, darum kann ich das was er über strukturierte Programmierung mit Funktionen sagt nicht 100%ig unterschreiben. Andererseits treffen fast alle Punkte die er über Klassen sagt auch auf Funktionen zu:
* Sie sind so konzipiert das man die innere Logik nicht kennen muss.
* Modifikationen innerhalb der Funktionen betreffen nicht den übrigen Code.
* Funktionen werden so entwickelt das sie jederzeit wiederverwendet werden können. Also so weit das möglich oder sinnvoll ist, das gilt aber auch für Klassen. Dieses pauschale Heilsversprechen von OOP das man dann alles irgendwie wiedervernden kann stimmt letztlich ja auch nicht.
Bei den letzten beiden Punkten stimmt der erste dann halt nicht mehr für Funktionen: Man kann sie nicht durch Vererbung erweitern.
Den letzten Punkt verstehen ich nicht: Klassen enthalten bereits eine Fehlerroutine? Häh?
Für Python-Programmierer ist es etwas zu PHP-syntaxlastig (man benutzt ``->`` aber braucht kein ``$``, der Gültigkeitsoperator ``::``, …).
Das Briefbeispiel ist zum schreiend wegrennen. Eine Klasse `BriefZustellen` mit den Attributen `adresse`, `inhalt`, und `versendet` und den Methoden `werfBriefEin()`, `sortierBriefEin()`, und `stellBriefZu()`? Argh.
Insgesamt gibt's da auch viel über ``private``, ``protected``, ``public`` was so nicht auf Python übertragbar ist. Inklusive der Getter-/Setter-Methoden die man in Python nicht schreiben würde.
Und es gibt die Klasse `Mensch` mit den Methoden `getName()` und `setName()` und davon abgeleitet `Frau` mit den Methdoden `setzNamen()` und `holeNamen()`, also *andere* Namen! WTF!?
Die Klasse `Sex` ist aus objektorientierter Sicht vollkommen sinnlos. Und wenn man Typprüfungen vornimmt ist das auch fast immer ein Zeichen das man OOP nicht so anwendet wie es vorgesehen ist. Die Frage ist nämlich an der Stelle warum es Klassen für `Mann` und `Frau` gibt und warum das Geschlecht nicht einfach eine Eigenschaft ist.
Bei den magischen Methoden ist fast alles nicht auf Python anwendbar.
Eine Klasse = eine Datei sollte man sich in Python dann ganz schnell wieder abgewöhnen.
Der Hinweis bei dem `Getraenk`-Beispiel: „Im Sinne der objektorientierten Programmierung ist das Beispiel ziemlich unglücklich“ aber „Es geht nur um das Verständnis.“ ist ja echt drollig. Was soll man denn da verstehen? Wie man es *nicht* macht? Dazu müsste dann doch aber dabei stehen warum man das nicht macht und wie man es machen würde, und dann bräuchte man das furchtbar schlechte Beispiel nicht. Keine Ahnung wofür man in PHP statische Methoden verwendet, aber in anderen Sprachen üblicherweise als alternative Konstruktoren. Insbesondere bei Sprachen bei denen es keine Überladung von Methoden gibt die anhand von Typsignaturen unterschieden werden können.
Schnittstellen lässt sich wieder nicht auf Python übertragen und abstrakte Klassen auch nicht so wirklich.
Iteratoren sind interessant, aber die Schnittstelle ist in PHP anders und deutlich umständlicher als in Python. Ausserdem ist das Beispiel schlecht gewählt. Da sollte man eher einen typischen Containertyp zeigen und keine Frau über deren Handtascheninhalt iteriert wird.
Ach so: Insgesamt ist der Text nichts für Frauen/Mädchen, es sei denn sie möchten gerne als Objekte betrachtet werden. Klasse hat der Text dort nicht wirklich…
Zwei kleine Einführungen zu OO mit Python. Beide fangen nicht mit den Schlüsselwörtern an, sondern beginnen mit dem Wozu.
http://inventwithpython.com/blog/2014/1 ... e-example/
http://reeborg.ca/docs/oop_py_en/index.html
http://inventwithpython.com/blog/2014/1 ... e-example/
http://reeborg.ca/docs/oop_py_en/index.html
- pillmuncher
- User
- Beiträge: 1527
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
Richtige Frauen/Mädchen/Männer/Jungs machen das so:Pfff! Objektorientierung mit Objekten.
Dank an Hal Abelson, von dem ich das gelernt habe.
Code: Alles auswählen
# constructor:
def address(first_name, last_name, street, zipcode, city):
def address_data(index):
if index == 0:
return first_name
elif index == 1:
return last_name
elif index == 2:
return street
elif index == 3:
return zipcode
elif index == 4:
return city
else:
raise AttributeError
return address_data
# selectors:
def first_name(address_data):
return address_data(0)
def last_name(address_data):
return address_data(1)
def street(address_data):
return address_data(2)
def zipcode(address_data):
return address_data(3)
def city(address_data):
return address_data(4)
def main():
addr = address('Donald', 'Duck', 'Erpelweg 13', '12345', 'Entenhausen')
assert 'Donald' == first_name(addr)
assert 'Duck' == last_name(addr)
assert 'Erpelweg 13' == street(addr)
assert '12345' == zipcode(addr)
assert 'Entenhausen' == city(addr)
if __name__ == '__main__':
main()
Dank an Hal Abelson, von dem ich das gelernt habe.
In specifications, Murphy's Law supersedes Ohm's.
pixewakb hat geschrieben: Ich kenne leider keine verständliche Einführung in die objektorientierte Programmierung mit Python, aber seinerzeit hatte mir die Einleitung von Peter Kropff - allerdings für php - sehr geholfen.
Ich find' "Python 3 Object Oriented Programming" macht keinen so schlechten Eindruck. Ob das Buch im Forum schon mal rezipiert wurde, weiß ich allerdings nicht.MagBen hat geschrieben:Zwei kleine Einführungen zu OO mit Python. Beide fangen nicht mit den Schlüsselwörtern an, sondern beginnen mit dem Wozu.
http://inventwithpython.com/blog/2014/1 ... e-example/
http://reeborg.ca/docs/oop_py_en/index.html