Forum: PC-Programmierung Python 3: Warum klappt die Initialisierung einer Klasse nicht?


von Bernd W. (berndwiebus) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo.

Folgender Code (Auch im Anhang als Klassentest.py enthalten):
1
# -*- coding: utf-8 -*-
2
import tkinter
3
from tkinter.filedialog import *
4
5
class Klassendef:
6
    def __init__(self):
7
        self.a = "Var a"
8
        self.b = "Var b"
9
        print ("initialisierung")
10
        print (self.a)
11
        print (self.b)
12
        print ("initialisierung ende")
13
    
14
    def funktion1(self):
15
        self.a = self.a + "+a"
16
17
    def funktion2(self):
18
        self.b = self.b + "+b"
19
20
print ("Vor Initialisierung")
21
22
class ersteKlasse (Klassendef):
23
    pass
24
25
class zweiteKlasse (Klassendef):
26
    pass
27
print ("initilasierung erste Klasse")
28
ersteKlasse.__init__(ersteKlasse)
29
30
print ("initilasierung zweite Klasse")
31
zweiteKlasse.__init__(zweiteKlasse)
32
  
33
34
35
print ("erste Klasse a = " + ersteKlasse.a)
36
ersteKlasse.funktion1(ersteKlasse)
37
print ("jetzt erste Klasse a = " + ersteKlasse.a)
38
39
print ("zweite Klasse a = " + zweiteKlasse.a)
40
zweiteKlasse.funktion1(zweiteKlasse)
41
print ("jetzt zweite Klasse a = " + zweiteKlasse.a)
42
43
ersteKlasse.a = "test1"
44
zweiteKlasse.a ="test2"
45
46
print (ersteKlasse.a)
47
print (zweiteKlasse.a)
48
49
ersteKlasse.funktion1(ersteKlasse)
50
print ("jetzt erste Klasse a = " + ersteKlasse.a)

funktioniert so (erst) einmal.

Die Idee ist, zwei Objekte, "ersteKlasse" und "zweiteKlasse" als 
abgeleitete Klassen aus "Klassendef" zu erstellen, und dann ersteinmal 
mit den gleichen Variableninhalten zu füllen.

dies geschieht in
1
class ersteKlasse (Klassendef):
2
    pass
3
class zweiteKlasse (Klassendef):
4
    pass
5
6
print ("initilasierung erste Klasse")
7
ersteKlasse.__init__(ersteKlasse)
8
9
print ("initilasierung zweite Klasse")
10
zweiteKlasse.__init__(zweiteKlasse)

Was ich irgendwie "unelegant" finde, ist das "pass". NOPs sind mir zwar 
aus Assembler wohlbekannt, aber in einer Hochsprache oder gar 
Skriptsprache (vermutlich) eher selten. Lasse ich die "pass" aber weg, 
bekomme ich die Fehlermeldung, das der Interpreter hier eine Einrückung 
erwartet, was eigentlich auch impliziert, das er zu class ..... 
passenden/folgenden code erwartet.

Nun gut, eigentlich könnte ich ja die anschliessenden Initialisierungen 
statt der "pass" dort einfügen, dann hätte ich die den passenden Code 
dort, das "pass" wäre weg, und mein Listing etwas eleganter.

Der entsprechende Abschnitt lautet dann:
1
class ersteKlasse (Klassendef):
2
    print ("initilasierung erste Klasse")
3
    ersteKlasse.__init__(ersteKlasse)
4
5
class zweiteKlasse (Klassendef):
6
    print ("initilasierung zweite Klasse")
7
    zweiteKlasse.__init__(zweiteKlasse)

Leider aber ist das FALSCH, weil ich nun die Fehlermeldung
1
>>> 
2
Vor Initialisierung
3
initilasierung erste Klasse
4
Traceback (most recent call last):
5
  File "/home/wiebus/python/Klassentest_2.py", line 22, in <module>
6
    class ersteKlasse (Klassendef):
7
  File "/home/wiebus/python/Klassentest_2.py", line 24, in ersteKlasse
8
    ersteKlasse.__init__(ersteKlasse)
9
NameError: name 'ersteKlasse' is not defined
10
>>>
bekomme, was heisst, die erhoffte Initialisierung hat überhaupt nicht 
stattgefunden, weil "ersteKlasse", welche ich durch "class 
ersteKlasse(Klassendef) definiert wähnte, überhaupt noch nicht 
existiert?????

Aber in der ursprünglichen Version mit "pass" existiert die Klasse nach 
verlassen der Klassendefinition?

Meine Intuition sagt mir jetzt, das ich einen dicken Denkfehler mache, 
aber wenn ja, wo und warum und wenn nein, wie geht es besser?

Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

PS: BS ist Debian squeeze.

von Norbert (Gast)


Angehängte Dateien:

Lesenswert?

Habe unter Squeeze zwar nur Python 2.6.something installiert, aber...

Soll es eventuell so wie im Anhang funktionieren?

von Bernd W. (berndwiebus) Benutzerseite


Lesenswert?

Hallo Norbert.

> Habe unter Squeeze zwar nur Python 2.6.something installiert, aber...
>
> Soll es eventuell so wie im Anhang funktionieren?

Nach Umbau auf Python 3 funktioniert es genauso wie die Ursprüngliche 
Version, und natürlich auch das angehängte "show".

Alledings ist mir jetzt noch weniger klar, was da jetzt passiert.
Das "pass" ist nicht weg, und die Initialisierung erfolgt jetzt durch 
das Zuweisen "ek = ersteKlasse()" und "zk = zweiteKlasse()"????

Mir fehlt dort wohl noch etwas Theorie, mein Lehrbuch scheint in dem 
Punkte etwas mager zu sein. Das Lehrbuch macht es so wie Du, erklärt 
aber auch nicht, warum.


Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

von Norbert (Gast)


Lesenswert?

So, habe mal python 3 in wheezy installiert und etwas vorbereitet;-)

Vielleicht wird der Ablauf so klarer:
1
#!/usr/bin/env python3
2
3
print('--->1')
4
5
class BasisKlasse(object):
6
    print('--->2')
7
    instzaehler = 0
8
    instzaehler2 = 0
9
10
    def __init__(self):
11
        print('--->5:%d' % self.instzaehler)
12
        print('--->5:%d' % BasisKlasse.instzaehler2)
13
        self.instzaehler += 1
14
        BasisKlasse.instzaehler2 += 1
15
16
    def dummy(self):
17
        print('dummy')
18
19
print('--->3')
20
21
class B(BasisKlasse):
22
    pass
23
24
class C(BasisKlasse):
25
    pass
26
    
27
print('--->4a')
28
29
BasisKlasse.dummy(None)
30
31
print('--->4b')
32
33
Instanz_basis = BasisKlasse()
34
35
print('--->6')
36
37
Instanz_b = B()
38
39
print('--->7')
40
41
Instanz_c = C()
42
43
print('--->8')
44
45
#--->1
46
#--->2
47
#--->3
48
#--->4a
49
#dummy
50
#--->4b
51
#--->5:0
52
#--->5:0
53
#--->6
54
#--->5:0
55
#--->5:1
56
#--->7
57
#--->5:0
58
#--->5:2
59
#--->8

_init_ wird erst aufgerufen, wenn eine Instanz einer Klasse gebildet 
wird.
Man kann auch schön sehen, das instzaehler in jeder Instanz einen 
eigenen lokalen Wert hat.
Die Variable instzaehler2 'gehört' allen Instanzen

von Rolf M. (rmagnus)


Lesenswert?

Bernd Wiebus schrieb:
> Was ich irgendwie "unelegant" finde, ist das "pass". NOPs sind mir zwar
> aus Assembler wohlbekannt, aber in einer Hochsprache oder gar
> Skriptsprache (vermutlich) eher selten.
1
// warte auf gesetztes Flag
2
while (flag ==0)
3
    ; /* <- Da ist das "nop" */

> Lasse ich die "pass" aber weg, bekomme ich die Fehlermeldung, das der
> Interpreter hier eine Einrückung erwartet, was eigentlich auch
> impliziert, das er zu class ..... passenden/folgenden code erwartet.

Vergleiche es mal mit C++. Wenn ich da schreibe
1
class Irgendwas
2
{

dann erwartet der Compiler, daß irgendwo auch mal ein
1
};

dazu kommt, also der Codeblock wieder geschlossen wird. Python hat aber 
keine Klammern für den Block, sondern nur die Einrückung. Also muß da 
nach dem Kopf der Klassendefinition (oder überall, wo ein Doppelpunkt am 
Zeilenende steht) was eingerücktes stehen, damit der Codeblock 
vollständig ist. Da du aber in diesem Fall nichts zum einrücken hast, 
brauchst du irgendwas, das du hinschreiben kannst, das aber keinen 
Effekt hat. Und dafür ist dann das pass da.

von Bernd W. (berndwiebus) Benutzerseite


Lesenswert?

Hallo Norbert, hallo Rolf.

Vielen Dank für Eure Antworten.

> So, habe mal python 3 in wheezy installiert und etwas vorbereitet;-)
> Vielleicht wird der Ablauf so klarer:

Danke, ich werde mit dem Code morgen mal was rumspielen. Er scheint mir 
ein paar zusätzliche Fragen zu klären...z.B. was hinterher die 
Unterschiede in den einzelnen Klassen, die unterschiedlich erzeugt 
wurden, sind.

Zu:
1
 def __init__(self):
2
        print('--->5:%d' % self.instzaehler)
3
        print('--->5:%d' % BasisKlasse.instzaehler2)
4
        self.instzaehler += 1
5
        BasisKlasse.instzaehler2 += 1

> Man kann auch schön sehen, das instzaehler in jeder Instanz einen
> eigenen lokalen Wert hat.
> Die Variable instzaehler2 'gehört' allen Instanzen

Uuups.....das erklärt mir auch eine andere Merkwürdigkeit. ;O)

> init wird erst aufgerufen, wenn eine Instanz einer Klasse gebildet
> wird.

Ah, aber _init_ wird dann automatisch aufgerufen, wenn ich z.b. mit 
"Instanz_b = B()" eine Instanz erzeuge. Ich muss NICHT extra 
"Instanz_b.__init__()" aufrufen.....das war mir auch nicht klar.



>> Was ich irgendwie "unelegant" finde, ist das "pass". NOPs sind mir zwar
>> aus Assembler wohlbekannt, aber in einer Hochsprache oder gar
>> Skriptsprache (vermutlich) eher selten.

> Vergleiche es mal mit C++. Wenn ich da schreibe

Ok,ok C bzw. C++ kann ich zwar noch weniger..... ;O)

> dazu kommt, also der Codeblock wieder geschlossen wird. Python hat aber
> keine Klammern für den Block, sondern nur die Einrückung. Also muß da
> nach dem Kopf der Klassendefinition (oder überall, wo ein Doppelpunkt am
> Zeilenende steht) was eingerücktes stehen, damit der Codeblock
> vollständig ist. Da du aber in diesem Fall nichts zum einrücken hast,
> brauchst du irgendwas, das du hinschreiben kannst, das aber keinen
> Effekt hat. Und dafür ist dann das pass da.

Ok. DAS leuchtet mir ein und beantwortet einen Teil meiner Frage.

Also sind "NOPs" in einer Hochsprache durchaus noch vorhanden, auch wenn 
man sie als solche kaum erkennt. ;O)

Irgendwie bin ich davon ausgegangen, das der Interpreter erkennt, daß 
auf das class keine Einrückung folgt, sondern ein neues class in 
gleicher Ebene, und eine Heuristik existiert, die erkennt, wie es 
gemeint ist....
möglicherweise würde das aber auch oft zu missverständlichen Situationen 
führen bzw. Fehler würden nicht erkannt,.....mit Compiler- oder 
Interpreterbau habe ich mich auch noch nie auseinandergesetzt.

Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

von Salewski, Stefan (Gast)


Lesenswert?

>Also sind "NOPs" in einer Hochsprache durchaus noch vorhanden, auch wenn
>man sie als solche kaum erkennt. ;O)

>Irgendwie bin ich davon ausgegangen, das der Interpreter erkennt, daß
>auf das class keine Einrückung folgt,

Mit "NOPs" hat das wenig zu tun -- oder würdest Du die Semikolons, mit 
denen in C das Ende einer Anweisung gekennzeichnet wird, als NOP 
bezeichnen?

Und das 'pass' sollte ja nur vorübergehend notwendig sein:

http://openbook.galileocomputing.de/python/python_kapitel_06_003.htm

Ruby ist in dieser Hinsicht wohl etwas smarter, aber natürlich auch 
nicht perfekt. Etwa dass Instance-Variablen stets mit @ beginnen müssen, 
das kann anfangs schon befremdlich wirken...

von Bernd W. (berndwiebus) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo Stefan.

>> Also sind "NOPs" in einer Hochsprache durchaus noch vorhanden, auch wenn
>> man sie als solche kaum erkennt. ;O)
> Mit "NOPs" hat das wenig zu tun -- oder würdest Du die Semikolons, mit
> denen in C das Ende einer Anweisung gekennzeichnet wird, als NOP
> bezeichnen?
>
Nicht? Ok, von c hab ich keine Ahnung. Da hab ich mich wohl geirrt.

>>Irgendwie bin ich davon ausgegangen, das der Interpreter erkennt, daß
>>auf das class keine Einrückung folgt,
> Und das 'pass' sollte ja nur vorübergehend notwendig sein:

Ok. ;O)

> Ruby ist in dieser Hinsicht wohl etwas smarter, aber natürlich auch
> nicht perfekt. Etwa dass Instance-Variablen stets mit @ beginnen müssen,
> das kann anfangs schon befremdlich wirken...

Hauptsache es ist eindeutig.....

Das mit den Einrückungen bei Python ist auch so eine Sache. Die Tiefe 
lässt sich über mehrere Bildschirmseiten hinweg nicht immer gut 
verfolgen.
Aber vieleicht ist das ja auch ein Wink mit dem Zaunpfahl, die 
Unterprogramme kürzer zu halten.
Mittlerweile habe ich mich auch daran gewöhnt.

Im Anhang befindet sich noch "KlassenDemo_09Jul2012.py". Da habe ich ein 
kleines Demo Programm geschrieben, und mit einfachen Fällen von Klassen 
herumgeübt und um ein Beispiel zu haben.

Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

von Norbert (Gast)


Angehängte Dateien:

Lesenswert?

Bernd Wiebus schrieb:
> Das mit den Einrückungen bei Python ist auch so eine Sache. Die Tiefe
> lässt sich über mehrere Bildschirmseiten hinweg nicht immer gut
> verfolgen.
> Aber vieleicht ist das ja auch ein Wink mit dem Zaunpfahl, die
> Unterprogramme kürzer zu halten.
> Mittlerweile habe ich mich auch daran gewöhnt.

Falls Du mit GNU/Linux arbeiten solltest, kann ich Dir 'kate' sehr 
empfehlen. Dort kann man neben vielen anderen Dingen wie
Syntax-Highlighting auch 'indentation lines' einstellen.
Sieht dann wie im Anhang aus (als Beispiel bash).

von Rolf Magnus (Gast)


Lesenswert?

Bernd Wiebus schrieb:
> Irgendwie bin ich davon ausgegangen, das der Interpreter erkennt, daß
> auf das class keine Einrückung folgt, sondern ein neues class in
> gleicher Ebene, und eine Heuristik existiert, die erkennt, wie es
> gemeint ist....

Sowas würde die Sprachregeln verkomplizieren, was man bei den meisten 
Sprachen versucht, zu vermeiden. Gerade eine Heuristik macht das ganze 
nur unnötig kompliziert.

Bernd Wiebus schrieb:
>> Mit "NOPs" hat das wenig zu tun -- oder würdest Du die Semikolons, mit
>> denen in C das Ende einer Anweisung gekennzeichnet wird, als NOP
>> bezeichnen?
>>
> Nicht? Ok, von c hab ich keine Ahnung. Da hab ich mich wohl geirrt.

Wie man's nimmt. Ein Semikolon für sich alleine könnte man durchaus als 
eine Art "NOP" ansehen. Aber ein NOP in Assembler ist ja eine echte 
Instruktion, die tatsächlich auf dem Prozessor ausgeführt wird und die 
dementsprechend auch etwas Speicher und Rechenzeit verbraucht. Das 
Semikolon in C dagegen taucht im erzeugten Maschinencode natürlich nicht 
mehr auf. Es ist ein rein sprachliches Konzept.
Bei Python und dem PASS ist es wieder etwas anders, da es als 
Interpreter die Anweisungen ja on the fly während der Ausführung 
durchgeht und interpretiert. Ausgeführt wird hier auch nichts, aber der 
Parser muß das PASS natürlich auswerten.

von Bernd W. (berndwiebus) Benutzerseite


Lesenswert?

Hallo Norbert.


> Falls Du mit GNU/Linux arbeiten solltest, kann ich Dir 'kate' sehr
> empfehlen. Dort kann man neben vielen anderen Dingen wie
> Syntax-Highlighting auch 'indentation lines' einstellen.
> Sieht dann wie im Anhang aus (als Beispiel bash).

Danke für den Tipp! Das ist für Python schon hilfrreich. Eine 
Einrückungsebenen übersehen, und man schreibt ein ganz anderes Programm 
und wundert sich....;O)

Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

von Bernd W. (berndwiebus) Benutzerseite


Lesenswert?

Hallo Rolf.

> Das  Semikolon in C dagegen taucht im erzeugten Maschinencode natürlich >nicht 
mehr auf. Es ist ein rein sprachliches Konzept.

Ok, soweit habe ich das jetzt verstanden.

> Bei Python und dem PASS ist es wieder etwas anders, da es als
> Interpreter die Anweisungen ja on the fly während der Ausführung
> durchgeht und interpretiert. Ausgeführt wird hier auch nichts, aber der
> Parser muß das PASS natürlich auswerten.

Ah, Ok.....in der Weise hatte ich mich nocht nicht damit 
Auseinandergesetzt.

Soviel ich weiss, gibt es in Python auch noch einen "Mittelweg" zwischen 
reinem Interpreter und reinem Compiler, bei dem Bytecode erzeugt wird, 
der dann weiterverwendet wird.
Es würde jetzt vermutlich an Details der Umsetzung liegen, ob das 
Äquivalent eines "pass" im Bytecode noch auftaucht oder nicht.

Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.dl0dg.de

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf Magnus schrieb:
> Bei Python und dem PASS ist es wieder etwas anders, da es als
> Interpreter die Anweisungen ja on the fly während der Ausführung
> durchgeht und interpretiert.

Da der Python-Quellcode vor der Ausführung in Bytecode compiliert wird,
fällt das pass komplett weg, wird also nicht in ein NOP übersetzt. So
gesehen ist pass tatsächlich eher mit der leeren Anweisung (;) in C
vergleichbar als mit dem NOP in Assembler.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.