Probleme mit OOP

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
Capone
User
Beiträge: 3
Registriert: Mittwoch 4. Januar 2006, 22:15
Kontaktdaten:

Moin,
bin neu hier :).
So, hab schon gesehn, dass das Thema OOP öfters schon aufgetaucht ist...verständlich wie ich finde. Also ich lese grade das Buch "Das Einsteigersemina - Python 2.x". Ist eigentlich ganz gut, nur viele Sachen werden zu kurz oder garnicht erklärt. Ich schreib hier einfach mal denn Quellcode der auch im Buch ist und stelle meine Fragen als Kommentar:

Code: Alles auswählen

class Korb:
	def __init__(self, inhalt=None):
		"Konstruktor"
		self.inhalt=[]    #wozu is das?
		if inhalt: self.inhalt.append(inhalt)
	def add(self, sache):
		self.inhalt.append(sache) # was soll das mit dem . immer?
	def show(self):
		for element in self.inhalt: # wieso steht das self überall vor?
			print element
Ich hoffe es macht nicht soviel Aufwand den Code kurz zu erklären. :)
antimicro
User
Beiträge: 151
Registriert: Sonntag 29. Februar 2004, 16:24

Hi Capone,

Code: Alles auswählen

self.inhalt=[]
ist eine neue und leere (wegen []) Liste. In Listen kannst du alles mögliche ablegen. Allerdings sollte es zum Variablennamen passen. z.B. self.filmnamen_liste[] etc.

Code: Alles auswählen

self.inhalt.append(sache)
Die Punkte weisen entweder auf Instanzen oder auf Methoden hin (wenn's falsch ist korrigiert mich). Eine Instanz benutzt man z.B. damit der Code geordneter ist. So weiß man z.B. das "inhalt" zu "self" gehört. "append()" ist eine Methode. Was sie macht erfährst du aus der Dokumentation zu Listen (self.inhalt war ja eine Liste). "append()" fügt die "sache" am Ende der Liste ein.

Code: Alles auswählen

for element in self.inhalt:
            print element 
Das "self" repräsentiert die Klasse selbst. "self" ist sollte immer der erste Parameter sein. Natürlich kannst du auch irgendeinen anderen Namen einsetzen, aber die meisten (vielleicht sogar alle) benutzen "self". Der Parameter verweist auf die Klasse selbst. Wenn du also im Konstruktor die klasseneigene Funktion "add" benutzen willst, dann schreibst du im Konstruktor "self.add()" (mit den entsprechendem Parameter natürlich).

Hättest du im Konstruktor nur "liste = []" geschrieben könntest du die Liste nicht in der Funktion "add" benutzen, weil "liste" dann nur im Konstruktor gültigkeit hat. Da "self.liste" zu "self" -und damit zu Klasse- gehört kannst du überall wo du die Klasse in Form von "self" bekommst auch "self.liste" benutzen.

Ach ja: Variablen sind Objekte... also nicht wundern wenn das in deinem Buch anders steht, aber beim Erklären finde ich das unpraktisch von Objekten zu reden.

Hoffentlich ist so alles richtig was ich hier schreibe :?
greetings
sebi
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

self ist in der Klasse eine Referenz auf sich selbst, eine Art namensraum. Dort finden sich alle Variablen, die die Instanz der Klasse hat.

Code: Alles auswählen

self.inhalt.append(irgendwas)
self ist die Klasse, Inhalt ist eine Variable (eigetnlich ist es ja ein Objekt) innerhalb dieser Klasse und append ist eine Funktion dieses Objektes.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Capone
User
Beiträge: 3
Registriert: Mittwoch 4. Januar 2006, 22:15
Kontaktdaten:

self ist die Klasse, Inhalt ist eine Variable (eigetnlich ist es ja ein Objekt) innerhalb dieser Klasse und append ist eine Funktion dieses Objektes.
Das versteh ich jetzt auch net ganz. Im Buch steht:
"[...]und mit ihm ist die konkrete Instanz gemeint, mit der wir im Augenblick arbeiten."
Und eine Instanz ist doch nicht die Klasse...oda wie? :?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Capone hat geschrieben:Das versteh ich jetzt auch net ganz. Im Buch steht:
"[...]und mit ihm ist die konkrete Instanz gemeint, mit der wir im Augenblick arbeiten."
Und eine Instanz ist doch nicht die Klasse...oda wie? :?
Du solltest Instanz und Klasse voneinander unterscheiden: es gibt Klassen die man sich als "Vorlagen" vorstellen kann. Dann gibt es Instanzen von Klassen, die eben die Objekte darstellen.

Code: Alles auswählen

class Vorlage(object):
    pass

inst = Vorlage()
Ich versuche es mal mit anderen Worten: du hast zwei Bälle, absolut identisch. Das sind beides Instanzen der Klasse Ball.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Vielleicht wird es verständlicher, wenn man sieht, was genau der Compiler eigentlich für Code erzeugt:

Angenommen, es gäbe kein OOP, und du hast irgendeine Datenstruktur, nehmen wir der Einfachheit einfach mal eine liste an. Und nun schreibst du Funktionen, die bestimmte Sachen mit der Liste machen. (Das ist besser, als es jedesmal von Hand einzufügen; solltest du mal etwas ändern müssen, müsstest du das ganze Programm ändern)

Code: Alles auswählen

def liste_add(liste, element)
Du hast nun eine ganze Menge Funktionen liste_add, liste_delete, liste_search etc. die alle mit einer Liste arbeiten. OOP vereinfacht das nur, intern passiert dassselbe.

Ich nehme jetzt mal kurz C++, warum, siehst du nachher noch:

Anstatt

Code: Alles auswählen

struct liste {
  ...elemente der liste...
};
void liste_add(liste* list, element el);
liste* liste_new();
[...]

// Hauptprogramm
liste* mylist = liste_new();
element myelem;
liste_add(mylist, myelem);
schreibst du es jetzt so:

Code: Alles auswählen

class Liste {
  void add(element el);
  Liste();
  ...elemente der liste...
};
[...]

Liste* mylist = new Liste();
element myelem;
mylist.add(myelem);
Intern passiert genau dasselbe:
Jede Funktion (die jetzt Methoden heißen) bekommt implizit einen Pointer auf die Klasseninstanz übergeben (ansonsten wüsste die Funktion ja nicht, womit sie arbeiten soll), dieser erster Parameter heißt immer "this". Desweiteren werden zugriffe auf elemente der Klasse innerhalb der Methoden automatisch mit this dereferenziert:

Code: Alles auswählen

void EineKlasse::EineFunktion()
{
   // x ist ein Element von EineKlasse
   x += 1; // Wird wie "this.x += 1" behandelt
}
Unter der Haube passiert also:

Code: Alles auswählen

Liste* mylist = konstruktor_der_Klasse_Liste();
element myelem;
Methode_add_der_Klasse_Liste(mylist, myelem)
In Python funktioniert das ganze genauso, nur musst du bei der Funktionsdeklaration den ersten Parameter, der die Klasseninstanz bekommt, explizit mit angeben. Das dieser Parameter "self" heißt, ist reine Konvention, du kannst ihn auch "this" nennen.

Da Python auch nicht automatisch "x" in "self.x" umwandelt, musst du es Python explizit sagen.

Zum Schluss mal ein Vergleich von C++ und Python:

Code: Alles auswählen

class Foo {
public:
  void Bar();
  int x;
};
// Der Konstruktor Foo() wird implizit erzeugt und setzt x auf 0.

void Foo::Bar() { //Intern Foo_Bar(Foo* this)
  int y; // lokal 
  y = x; //intern y = this.x;
  x += y; // this.x += y
}

Code: Alles auswählen

class Foo:
  def __init__(self):
     self.x = 0
  def Bar(self):
     y = self.x
     self.x += y
Capone
User
Beiträge: 3
Registriert: Mittwoch 4. Januar 2006, 22:15
Kontaktdaten:

Ich versteh jetzt so ziemlich alles, bis (immernoch) auf dieses 'self'...das treibt mich noch in den Wahnsinn! :evil:
Tut mir echt leid, dass ich anscheinend so lange brauche bis ich das begreife.
Also ihr sagt, dass self die Klasse ist. Aber wenn ich jetzt in meinem Buch gucke oder in Tutorials, dann les bzw versteh ich etwas anderes. Guckt euch das doch bitte mal an:
http://64.233.183.104/search?q=cache:cg ... =firefox-a
Da steht folgender Satz: "Dieser self-Parameter ist mit dem Namen der Instanz identisch5.3"
Und wenn ihr dann noch auf dieses "5.3" klickt, steht da das:"Und nicht mit dem Namen der Klasse!!"
Ich komm so einfach nicht weiter und ich will OOP auch nicht überspringen, also probiert noch mal mir das zu erklären.^^
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Ja, da hab ich etwas schlurig geschrieben. Self ist die Instanz.

Beispiel:

Code: Alles auswählen

class Foo:
  def print_self(self):
    print self

a = Foo()
print a
a.print_self()
Beide Anweisungen geben dasselbe aus.

Der Grund, warum du self mit angeben musst ist übrigens die dynamische Natur von Python. Du kannst, wenn du denn möchstest, die Klasse zur Laufzeit umdefinieren! (In der Regel zwar nicht empfehlenswert, aber dennoch möglich)
Beispiel:

Code: Alles auswählen

class Foo:
  def bar(self):
    pass # nichts machen

a = Foo()
a.bar() # nichts passiert
def mein_print(x):
  print x
Foo.bar = mein_print # die Methode bar ist jetzt mein_print
a.bar() # gibt die Instanz aus, da der Funktion als ersten Parameter self übergeben wird
Dasselbe geht übrigens auch andersrum:

Code: Alles auswählen

class Foo:
  def bar(self):
    print self

mein_print = Foo.bar
mein_print("Hallo, Welt") # gibt Hallo, Welt aus
BlackJack

Ich möchte es auch nochmal versuchen :-)

Also, alles in Python sind Objekte. Objekte können an Namen gebunden sein und Attribute haben. An diese Attribute (auch wieder Objekte) kommt man mit dem Punkt-Operator heran:

Code: Alles auswählen

print person.name
Hier wird erst das `person` Objekt gesucht und "in" diesem Objekt dann das Attribut `name`. Was wahrscheinlich eine Zeichenkette ist. Zeichenketten haben auch wieder Attribute, zum Beispiel eine Methode, die eine Zeichenkette mit dem gleichen Inhalt, aber in Grossbuchstaben zurückgibt und diese Methode heisst `upper`:

Code: Alles auswählen

In [5]: print person.name.upper
<built-in method upper of str object at 0x404b7660>
Huch, was ist nun passiert? Naja wir haben die Methode nicht aufgerufen, sondern die Methode selbst ausgegeben. Funktionen und Methoden sind auch nur Objekte und so sieht eine Methode aus, wenn man sie als Zeichenkette ausgibt. Also noch die Klammern ans Ende geschrieben, damit sie auch aufgerufen wird:

Code: Alles auswählen

In [6]: print person.name.upper()
PETER
So, nun zum `self`. Methoden unterscheiden sich durch Funktionen dadurch, das sie an Objekte gebunden sind, genauer gesagt an Instanz-Objekte von Klassen. Das bedeutet nichts weiter als das ein Objekt weiss von welcher Klasse es ist und bei einem Methodenaufruf auf einem Objekt wird die Methode in der Klasse aufgerufen, wo sie nur eine Funktion ist, mit dem Objekt selbst als erstem Parameter.

Kleines Beispiel:

Code: Alles auswählen

class Person:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname
    
    def speak(self):
        print 'Hello, my name is %s %s.' % (self.name, self.surname)

agent = Person('James', 'Bond')
agent.speak()
Person.speak(agent)
Die beiden letzten Zeilen sind äquivalent. Da das Objekt weiss von welcher Klasse es eine Instanz ist, wird in beiden Fällen die Funktion `Person.speak()` aufgerufen. Der Unterschied und Vorteil der ersten Variante ist, das der Programmierer an der Stelle nicht wissen muss, das es sich um ein `Person` Objekt handelt und das man den ersten Parameter nicht selbst übergeben muss.

Das man als Programmierer nicht die genaue Klasse wissen muss, erlaubt "Polymorphie", d.h. das man veschiedene Klassen mit dem gleichen Code benutzen kann. Mal angenommen man definiert noch eine Haustierklasse die auch "sprechen" kann:

Code: Alles auswählen

class Pet:
    def __init__(self, name, sound):
        self.name = name
        self.sound = sound
    
    def speak(self):
        print '*%s*' % self.sound

dog = Pet('Lassie', 'wuff')
dog.speak()
Dann funktioniert folgender Quelltext nicht:

Code: Alles auswählen

for creature in (agent, dog):
    Person.speak(creature)
Wenn der Hund eingesetzt wird, dann kommt es zu einem `TypeError`:

Code: Alles auswählen

TypeError: unbound method speak() must be called with Person
instance as first argument (got Pet instance instead)
Wenn man das `creature` selbst entscheiden lässt, ob `Person.speak()` oder `Pet.speak()` benutzt werden soll, dann klappts:

Code: Alles auswählen

for creature in (agent, dog):
    creature.speak()
studi
User
Beiträge: 25
Registriert: Montag 2. Juni 2008, 22:59

Guten Tag.

Erstmal danke fuer die oberen Erklaerungen, habe viel daraus gelernt.
Jetzt bin ich soweit:

Code: Alles auswählen

import threading
from timeit import Timer

class Foo:
    def __init__(self):
        None
    def add(self, x, y):
        i = x + y
        global t
        t = threading.Timer(5.0, a.add)
        t.start()
        print i
        return i

a = Foo()
print a.add(4, 4)
Wenn ich das Prog. ohne thread starte gehts, doch mit thread gibt er folgendes aus:

Code: Alles auswählen

8
>>> Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Programme\Python25\lib\threading.py", line 460, in __bootstrap
    self.run()
  File "C:\Programme\Python25\lib\threading.py", line 625, in run
    self.function(*self.args, **self.kwargs)
TypeError: add() takes exactly 3 arguments (1 given)
Was will er haben ich uebergebe ihm doch mit

Code: Alles auswählen

a.add(4, 4)
alles was er braucht. Ich werd die threads wohl nie verstehen. :(
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Das Problem liegt woanders, nämlich in Zeile 10.
Du musst die Argumente für die Funktion als Liste übergeben:

Code: Alles auswählen

t = threading.Timer(5.0, a.add,[x,y])
studi
User
Beiträge: 25
Registriert: Montag 2. Juni 2008, 22:59

Danke pütone, jetzt geht's. :)
Antworten