Datentyp explizit zuweisen

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.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

NoPy hat geschrieben:Schritt 1) Festgestellt Syntax-Fehler werden protokolliert ... Das ist bei mir so nicht. Wenn ich an keinem Syntaxfehler vorbeikomme, wird nichts protokolliert. Protokolliert wird erst, wenn die fehlerhafte Zeile benutzt wird - also zur Laufzeit ;)
Der folgende Code enthält einen Syntaxfehler, aber im Programmablauf wird die Zeile nicht verwendet.

Code: Alles auswählen

if False:
    = 1
Ich bekomme da folgende Meldung:

Code: Alles auswählen

    = 1
    ^
SyntaxError: invalid syntax
Ganz offensichtlich läuft der Compiler vor der Codeausführung.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Kurz vorangestellt: Python ist für mich sicher nicht das Mittel der Wahl. Ich habe bislang für triviale Dinge, die ich beispielsweise in Delphi in wenigen Stunden fertig gehabt hätte, schon Tage verbracht. Das mag vor allem daran liegen, dass ich Python noch nicht so gut kenne UND 20 Jahre lang anders programmiert habe. Da ich aber eine Software erweitern muss, die nur Python (2.7) kann, bleibt mir nur Python.

Das heißt nicht, dass ich nicht gewillt bin, zu lernen. Daher habe ich auch mit Interesse Eure Antworten verfolgt, vielen Dank dafür. Ich bin mir nicht sicher, ob ich sie immer richtig verstanden habe (Beispielsweise: Meint ihr mit Klassenvariable das gleiche, wie ich? Einen Member der Klasse, der existiert, ohne dass auch nur eine einzige Instanz existiert, einen Instanz- Zähler o.ä.? )

Ich glaube im Moment nicht, dass python meine Lieblingssprache wird.

Aber weil Du es schon erwähnst:

Code: Alles auswählen

class XYZ(object):
  S=1
  __init__(self):
    self.T=2
Was genau ist der Unterschied zwischen S und T?
Wo müsste man eine Klassenvariable bzw. eine Klassenmethode deklarieren?

Zu den übrigen Anmerkungen: Es ging hier natürlich nicht um reale Deklarationen, sondern um Illustrationen meiner "Sorgen und Nöte"
Wenn ich ein konkretes Beispiel habe, werde ich mich damit bei Euch melden. Im Moment halfen mir Eure Ausführungen schon mal, einige Klarheiten zu schaffen, die mir erst durch sehr langes Lesen in Dokumentationen zuteil geworden werden.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ja, eine Klassenvariable existiert und ist verfuegbar, ohne dass es ein Exemplar der Klasse gibt.
So kann man auf `S` mittels `XYZ.S` zugreifen, aber `XYZ.T` gibt es nicht. Aufgrund der Lookuphierarchie kann man dagegen ueber `xyz.S` auf `S` zugreifen, worauf man sich aber nicht verlassen sollte, denn eine Exemplarvariable `S` kann die der Klasse ueberdecken (jedem der das tut sollte man aber auf die Finger hauen).

Klassen- und statische Methoden deklariert man mit den Funktionen `classmethod` und `staticmethod`.

Von einer Klassenvariable, die die Anzahl der Exemplare zaehlt, sollte man aber Abstand nehmen, weil man dazu unweigerlich einen Destruktor braucht. Den gibt es zwar mit ``__del__``, aber die Semantik garantiert nicht den Zeitpunkt, wann die Methode aufgerufen wird (oder gar ob!).
Der Zustand ist hier also derselbe wie bei anderen Sprachen mit GC.

Ich verstehe zwar dass man mit "Sorgen und Noeten" aus statisch typisierten Sprachen zu Python kommt, aber wie pillmuncher in deinem anderen Thread angedeutet hat, sollte man sich auf andere (Programmier-)Sprachen einlassen und sie nicht gleich behandeln.
BlackJack

@NoPy: Mit ``class XYZ`` wird ein Klassenobjekt erzeugt und an den Namen `XYZ` gebunden. Auf der Ebene der Klasse definierst Du zwei Attribute `S` und `__init__` (ich gehe mal davon aus da sollte ``def`` davor stehen :-)). Diese beiden Attribute sind von der Klasse abfragbar, ganz normal per Attributzugriff.

Wenn man `XYZ` aufruft wird ein Objekt vom Typ `XYZ` erstellt und mit diesem Objekt als Argument wird die `__init__`-Methode aufgerufen. Und dort wird auf dem Objekt das Attribut `T` gebunden.

Danach kann man auf die Attribute des Objektes zugreifen, also direkt auf die Attribute auf dem Objekt, und wenn ein Attribut dort nicht gefunden wird, dann wird in der Klassenhierarchie danach auf den Klassenobjekten gesucht. Also haben auch Objekte vom Typ `XYZ` indirekt ein `__init__`- und ein `S`-Attribut. Eben das vom Klassenobjekt. Wo man sehr aufpassen muss ist beim Zuweisen, denn wenn man einem Objekt ein Attribut zuweist landet das immer auf *dem* Objekt, auch wenn es in der Klassenhierchie ein Attribut mit dem selben Namen gibt:

Code: Alles auswählen

In [1]: class A(object):
   ...:     a = 1
   ...:     def __init__(self):
   ...:         self.b = 2
   ...: 

In [2]: A.a
Out[2]: 1

In [3]: x, y = A(), A()

In [4]: x.a
Out[4]: 1

In [5]: y.a
Out[5]: 1

In [6]: x.b, y.b
Out[6]: (2, 2)

In [7]: A.a = 42

In [8]: x.a, y.a
Out[8]: (42, 42)

In [9]: x.b = 23

In [10]: x.b, y.b
Out[10]: (23, 2)

In [11]: y.a = 4711

In [12]: A.a, x.a, y.a
Out[12]: (42, 42, 4711)
In [11] wird dem Objekt das an `y` gebunden ist ein neues `a`-Attribut hinzugefügt und nicht etwa das `a`-Attribut auf dem Klassenobjekt neu gebunden. Wenn man das will muss man auch explizit über das Klassenobjekt gehen wie in [7].

Wir meinen also mit Klassenvariable oder Klassenattribut das selbe wie Du (eventuell etwas mehr weil Methoden auch Attribute sind, wo andere Sprachen manchmal stärker zwischen Daten und Methoden unterscheiden). Und ein Instanz-Zähler ist ein schlechtes Beispiel weil der nicht zuverlässig funktionieren würde, zumindest dann nicht wenn Objekte auch wieder „verschwinden” müssen.

Ergänzend zu cofi: Ängste und Nöte an konstruierten Beispielen haben oft auch das Problem dass sie an der Realität vorbei gehen. Es gibt sicher oft Lösungen zu konkreten Problemen die komplett anders aussehen als das konstruierte Beispiel und damit das Problem im Beispiel gar nicht erst auftritt. Zum Beispiel das man zum Mischen gar keinen extra allgemeinen Containertyp braucht, sondern `random.shuffle()` mit jedem Typ klar kommt, der sich wie eine Sequenz verhält. Egal ob das nun konkret ein Typ ist der Karten oder Dominosteine verwaltet, oder sonst irgend etwas.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Mag sein, dass es mit der shuffle- Funktion eine Möglichkeit gibt, das Anschauungsbeispiel zu lösen.
Aber das ist natürlich nicht zielführend. Ich habe ja versucht, ein komplexeres Problem auf ein allgemein Verständliches herunterzubrechen.

Nichts desto trotz haben Eure Antworten viel KONKRET erklärt, was ich sonst nur in mühsamer Kleinstarbeit herausgefunden hätte. Was ich allerdings durchaus verwirrend finde, ist, dass y.a = 4712 eine Membervariable anlegt und nicht die Klassenvariable belegt. Aber gut, das sind Feinheiten, die ich bislang selten brauchte.
BlackJack

@NoPy: Ja, das mit dem Beispiel war mir schon klar, das Problem ist halt immer das man solche Beispiele vereinfachen kann, es aber auch irgendwelchen nicht bekannten, willkürlichen Gründen nicht darf. Darum kann man da IMHO schlechter drüber reden als über konkrete Beispiele wo klar ist, warum man das nicht anders lösen kann. Das wirkliche Problem mit dem Beispiel war aber das es das Problem gar nicht illustriert.

Andere wären verwirrt wenn ``x.a = 42`` nicht das Attribut anlegen würde wenn es nicht existiert und den Wert für *alle* Objekte mit dem gleichen Typ ändern würde. Da ist es doch deutlicher wenn man beim Lesen schon sieht, dass der Wert auf dem Klassenobjekt neu gebunden wird. Setzen und anlegen von Attributen auf dem konkreten Objekt ist ja auch der Fall den man in 99,9% der Fälle haben möchte.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

NoPy hat geschrieben:Nichts desto trotz haben Eure Antworten viel KONKRET erklärt, was ich sonst nur in mühsamer Kleinstarbeit herausgefunden hätte. Was ich allerdings durchaus verwirrend finde, ist, dass y.a = 4712 eine Membervariable anlegt und nicht die Klassenvariable belegt. Aber gut, das sind Feinheiten, die ich bislang selten brauchte.
Das empfindest du nur verwirrend, weil du nicht gewohnt bist, dass Klassen vollwertige Objekte sind. Wenn wir von Klassenmethoden oder Klassenvariablen sprechen meinen wir deswegen *nicht* statische „Methoden“ oder Variablen wie in C++ oder Java. Das ist nicht dasselbe. Klassenmethoden und -variablen bedingen OOP+Vererbung auf Seiten der Klassenhierarchie. Der Unterschied ist einfach daran zu erkennen, dass Klassenmethoden ein "self" oder "cls" haben das auf die Klasse verweist, während statische „Methoden“ die Klasse explizit benennen (müssen).

Code: Alles auswählen

class SuperClass(object):
    attribute = "SuperClass"
    
    @classmethod
    def class_method(cls):
        return cls.attribute
    
    @staticmethod
    def static_method():
        return SuperClass.attribute

class Klass(SuperClass):
    attribute = "Klass"

print Klass.class_method()
print Klass.static_method()
C++ oder Java (und anscheinend auch Delphi) sind deswegen imho ungeeignete Sprachen um OOP zu lernen. Ich verweise deswegen immer wieder gerne auf Smalltalk was das angeht.
Antworten