@MoonKid: Warum willst du immer C++ in Python programmieren? Programmier halt einfach C++, wenn dir das besser gefällt. Es muss ja nicht jedem alles gleich gut gefallen. Falls du Python weiter verwenden möchtest, solltest du jedoch anfangen, das Wort
dynamisch in dem Satz
Python ist eine dynamische Programmiersprache ernst zu nehmen. Ich hab es schon oft geschrieben: Python ist kein C++ mit weniger Klammern, sondern eher eine Art Lisp mit weniger Klammern. Wobei ich leider zugeben muss, dass ich am Anfang auch etwas gebraucht habe, bis ich die Konsequenzen davon verstanden hatte. Ich kam auch von statisch typisierten, undynamischen Sprachen wie C++, C# und Java (das war damals noch sehr undynamisch) und brauchte einige Zeit, bis ich mich von den dort geltenden Konventionen gelöst hatte. Seit ich es verstanden habe, habe ich allerdings nie mehr zurückgeblickt. Wenn ich jetzt daran denke, dass ich mal wieder was mit statischer Typisierung programmieren möchte, dann denke ich zB. an Haskell oder OCaml oder sowas.
Sehr geholfen hat mir damals das
Python Cookbook, 2nd Ed. Inzwischen gibt es die
3rd Ed., aber ich habe noch nicht reingeschaut. Diese wurde von Dave Beazley besorgt, deswegen vermute ich, dass sie exzellent ist. Auch die Vorträge von Beazley auf Youtube sind alle hervorragend und zudem auch sehr lustig. Er ist mein Python-Held. Wenn du mal drei Stunden Zeit hast, schau dir das hier an:
Python 3 Metaprogramming.
Zu Deiner Frage - schau dir mal das hier an:
Code: Alles auswählen
>>> class Foo:
... x = 1
...
>>> Foo.x
1
>>> Foo.x = 2
>>> Foo.x
2
>>> f = Foo()
>>> f.x
2
>>> f.x = 3
>>> f.x
3
>>> g = Foo()
>>> g.x
2
>>> Foo.x
2
>>> Foo.x = 4
>>> g.x
4
>>> f.x
3
>>> del f.x
>>> f.x
4
Auf Klassenattribute kann man lesend nicht nur über die Klasse, sondern auch über deren Instanzen zugreifen. Schreibend kann man auf Klassenattribute nur über die Klasse zugreifen, aber nicht über die Instanzen. Manchmal verwendet man ein Klassenattribut als Defaultwert, das in einzelnen Instanzen mit einem Instanzattribut überschrieben (d.h.: überdeckt) werden kann, so wie ich das am Ende des Beispiels oben zeige.
Das alles ist ein Artefakt dessen, wie der Zugriff auf Methoden (die ja auch nur - zufällig aufrufbare - Klassenattribute sind) funktioniert. Guckstu:
Code: Alles auswählen
>>> class Bar:
... def blubb(self, v):
... print('-->', v)
...
>>> b = Bar()
>>> b.blubb(123)
--> 123
>>> b.blubb = lambda v: print('-->', 0)
>>> b.blubb(123)
--> 0
>>> c = Bar()
>>> c.blubb(123)
--> 123
Ein Attribut wird zuerst in der Instanz gesucht, und falls es dort nicht gefunden wird, in der Klasse, und falls es dort auch nicht gefunden wird, aufwärts entlang der mro-Liste, bis es entweder gefunden wird, oder nicht, woraufhin ein AttributeError fliegt:
Code: Alles auswählen
>>> class Base:
... zig = 1
...
>>> class Derived(Base):
... zag = 2
...
>>> b = Base()
>>> b.zig
1
>>> b.zag
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Base' object has no attribute 'zag'
>>> d = Derived()
>>> d.zig
1
>>> d.zag
2
>>> d.test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Derived' object has no attribute 'test'
>>> Base.mro()
[<class '__main__.Base'>, <class 'object'>]
>>> Derived.mro()
[<class '__main__.Derived'>, <class '__main__.Base'>, <class 'object'>]
Interessanterweise ist
mro() Ein Attribut der Metaklasse, deswegen kann man über die Instanz nicht darauf zugreifen:
Code: Alles auswählen
>>> b.mro()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Base' object has no attribute 'mro'
>>> d.mro()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Derived' object has no attribute 'mro'
Es ist völlig logisch und konsistent, dass das so sein muss, denn eine Instanz einer Klasse ist keine Instanz der Klasse einer Klasse - AKA Metaklasse:
Code: Alles auswählen
>>> class MyMeta(type):
... jause = 1
...
>>> class MyClass(metaclass=MyMeta):
... sause = 2
...
>>> mc = MyClass()
>>> mc.sause
2
>>> mc.jause
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'jause'
>>> MyClass.sause
2
>>> MyClass.jause
1
>>> type(Bar)
<class 'type'>
>>> type(MyClass)
<class '__main__.MyMeta'>
>>> type(mc)
<class '__main__.MyClass'>