Aufruf von Funktionen innerhalb der Klasse

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.
Gelöscht0903

Moin zusammen!

Ich habe eine Frage zum Aufruf von Funktionen innerhalb derselben Klasse.
Wenn das notwendig ist, dann habe ich das bisher so gelöst:

Code: Alles auswählen

def main():
	oldValue = 3.
	print('oldValue',oldValue)
	newValue = myClass.calculate(oldValue)
	print('newValue',newValue)
	
class myClass():
		
	def calculate(a):
		return myClass.doubleValue(a)
	
	def doubleValue(a):
		return 2.*a
Jetzt hat mir ein Kollege gesagt, der Aufruf myClass.doubleValue wäre so nicht i.O., weil das u.U. zu unerwünschten Effekten führen kann.
Ich sollte besser self.doubleValue verwenden.
Ich habe etwas im Internet rumrecherchiert, aber die Antwort nicht wirklich verstanden.
Auch habe ich es nicht geschafft den Code mit self.doubleValue zum laufen zu bekommen.
Hat mein Kollege recht und falls ja: wie sieht die Lösung dafür aus?
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@BowMaster: Du hast da überhaupt gar keine Klasse. Also formal, aber die macht überhaupt gar keinen Sinn. Klassen sind nicht dazu da um einfach nur Funktionen zu enthalten sondern um Zustand und Funktionen die darauf operieren zusammen zu fassen. Es gibt aber gar keinen Zustand und es wird auch nie ein Exemplar von dieser ”Klasse” erstellt.

Du hast da einfach nur zwei Funktionen die gar nicht in einer Klasse stecken sollten. Wenn eine Methode `self` nicht benutzt, dann recht was komisch. Und wenn es `self` nicht einmal gibt (und das keine `classmethod` oder `staticmethod` ist), dann ist da was falsch.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Der Präfix `my` macht in 99,99% der Fälle keinen Sinn. Wenn es nicht auch `OurClass` und/oder `TheirClass` gibt wovon sich `MyClass` abgrenzen würde, enthält das `My` keinerlei nützliche Information.

Code: Alles auswählen

#!/usr/bin/env python3


def double_value(value):
    return 2 * value


def calculate(value):
    return double_value(value) + 1


def main():
    old_value = 3
    print("old_value:", old_value)
    new_value = calculate(old_value)
    print("new_value:", new_value)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Gelöscht0903

Hi!
Danke für die Reaktion, aber das ist quasi Pseudocode, einfach um zu illustrieren, worum es mir geht:
den Aufruf einer Funktion, die in einer Klasse definiert ist, innerhalb dieser Klasse.

Auf die eigentliche Frage, ob der Aufruf self.function besser ist (und warum) als classname.function gehst Du leider nicht ein.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BowMaster hat geschrieben: Mittwoch 7. Juli 2021, 16:28 Danke für die Reaktion, aber das ist quasi Pseudocode, einfach um zu illustrieren, worum es mir geht:
den Aufruf einer Funktion, die in einer Klasse definiert ist, innerhalb dieser Klasse.
Ist dir der Unterschied zwischen einer Klasse und einer Instanz dieser Klasse klar?

Mit dem Minimalbeispiel das du bisher geliefert hast kann man nur sagen: da sollte überhaupt keine Klasse sein.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn man nur irgendwelchen Kram hinschreibt, der das eigentliche Problem nicht darstellt, dann ist es fuer diejenigen, die hier eine Frage beantworten sollen, auch noch ein bisschen extra schwer, das erwartungsgemaess zu tun. Tatsache ist, dass dein Code oben nicht funktioniert, und man raten muss, worauf es denn nun ankommt. Geschweige denn, wie eine Loesung aussaehe.

Du hast du auch nicht recht, __blackjack__ hat deine Frage sehr wohl beantwortet. Denn auf die Art und Weise wie du bisher Klassen benutzt, benutzt du faktisch keine Klassen. Sondern Funktionen mit globalem Zustand. Und das ist schlecht, da hat dein Kollege schon recht. Wegen der globalen Variablen, die den Programmablauf schwer bis unmoeglich nachvollziehbar machen.

Wenn man nur Funktionen braucht fuer ein Problem, dann benutzt man auch nur Funktionen. Wenn man Zustand braucht, der nicht einfach nur als Argumente und Rueckgabewert der Funktionen gehandhabt wird, dann braucht man Klassen. Und die sehen wie in jedem Tutorial zu sehen so aus:

Code: Alles auswählen

class Test:

    def __init__(self, argument):
        self.argument = argument
        self._irgendwas = None # alle Attribute der Klasse sollten in __init__ angelegt werden
class Test:

    def __init__(self, argument):
        self.argument = argument
        self._irgendwas = None # alle Attribute der Klasse sollten in __init__ angelegt werden

    def machwas(self, anderes_argument):
        if self._irgendwas is None:
            self._irgendwas = True
            anderes_argument = anderes_argument * 2
        self.argument += anderes_argument
        return self.argument


test = Test(50)
print(test.machwas(100))
print(test.machwas(200))
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

BowMaster hat geschrieben: Mittwoch 7. Juli 2021, 16:28 Hi!
Danke für die Reaktion, aber das ist quasi Pseudocode, einfach um zu illustrieren, worum es mir geht:
den Aufruf einer Funktion, die in einer Klasse definiert ist, innerhalb dieser Klasse.

Auf die eigentliche Frage, ob der Aufruf self.function besser ist (und warum) als classname.function gehst Du leider nicht ein.
Naja, da müssen wir einen Schritt zurückgehen: nämlich zum Unterschied zwischen einer Klasse und einer Instanz der Klasse. In

Code: Alles auswählen

class X:
    def __init__(self, bums):
        self.dings = bums
        
    def tuwas(self):
        return self.dings
        
i = X('lalala')
print(i.tuwas())
ist X (großes X) eine Klasse -- also: eine Vorschrift, wie eine Instanz dieser Klasse erzeugt wird -- und i (kleines i) eine Instanz, also ein konkretes Speicherobjekt, ebendieser Klasse.

Nun gibt es unter Python noch ein zweite kleine Besonderheit: bei einer Methode -- also: einer Funktion, die zu einer Klasse gehört -- wird die Instanz der Klasse bei der Methodendeklaration explizit erster Parameter mit angegeben, üblicherweise mit dem Namen "self" -- man kann da etwas anderes benutzen, wenn man von wütenden Menschenmengen mit Fackeln, Mistforken, Teer und Federn verfolgt werden möchte. Andere Sprachen wie Java und C++ machen das implizit mit einem ihrem Schlüsselwort "this".

Außerdem hat so eine Klasse ja einen tieferen Sinn: in der Regel soll sie Daten (Eigenschaften, Attribute oder Zustände genannt) und die Methoden, die sie bearbeiten, quasi zusammenfassen. Das heißt: eine Methode muß die Daten, die eine andere Methode der Klasse in der konkreten Instanz dieser Klasse gesetzt hat, lesen können -- es braucht also die besagte Referenz auf die Instanz, die beim Aufruf der Methode automatisch als erster Parameter übergeben wird.

Auf mein gerade gezeigtes Beispiel angewendet, ist also "dings" eine Eigenschaft der Klasse X. Der Wert wird dem Konstruktor __init__() als "bums" übergeben. Im Konstruktor wird dieser Wert dann der Instanzvariablen "dings" übergeben, die aber an der besagten Instanz hängt und deswegen mit "self.dings" angesprochen wird. Genau deswegen kann die Methode "tuwas()" auf der Instanz der Klasse aufgerufen werden (i.tuwas()), die ebenfalls als ersten Parameter die Instanz mitgeteilt bekommt: beim Aufruf von "i.tuwas()" ist der Parameter "self" tatsächlich das, was vor dem Punkt steht, nämlich die Instanz "i". Im Grunde genommen ist das also quasi eine Vereinfachung von "X.tuwas(i)" -- das geht zwar tatsächlich auch (Python ist sooo flexibel), aber... warum?

Ansonsten haben meine geschätzten Vorposter völlig Recht, und auch Dein Kollege liegt richtig: bitte lies mal ein oder zwei Python-Tutorials, und werd Dir über die Unterschiede und Zusammenhänge zwischen Klassen und Instanzen klar. Das sind fundamentale und sehr wichtige Dinge, die Du unbedingt verstehen und beherrschen solltest.
Gelöscht0903

Na, das ist ja kommunikativ maximal schiefgelaufen...

Also neuer Anlauf:

Ich habe mal das Klassenbeispiel etwas editiert:

Code: Alles auswählen

class Test:

    def __init__(self):
         pass
  
    def machwas(self, anderes_argument):
        return self.machwasanderes(anderes_argument)

    def machwasanderes(self,noch_ein_argument):
        return 2. * noch_ein_argument

class Test1:

    def __init__(self):
         pass

    def machwas(self,anderes_argument):
        return Test1.machwasanderes(anderes_argument)

    def machwasanderes(noch_ein_argument):
        return 2. * noch_ein_argument

test = Test()
print(test.machwas(100))

test1 = Test1()
print(test1.machwas(100))
Um damit zur ursprünglichen Frage zurückzukommen:
Was für Fallstricke lauern in Test1? Oder ist das einfach nur eine andere Art die Funktion machwasanderes in der Klasse aufzurufen?

P.S. Wäre schön, wenn wir die Sinnhaftigkeit, ob hier eine Klasse notwendig ist oder nicht, mal außer Acht lassen. Es geht einfach nur darum, ob die Art des Funktionsaufrufes Probleme bereiten kann und unter welchen Umständen
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@BowMaster: Die Sinnhaftigkeit der Klassen kann man nicht ausser Acht lassen, denn schon bei der Frage „Funktionen in Klassen aufrufen“ ist die Antwort man steckt in aller Regel keine Funktionen in Klassen, also sind auch eventuell daraus entstehende Probleme irrelevant. Denn wenn man diesen Unsinn nicht macht, kann es da auch keine Probleme geben.

Auch in dem neuen Beispiel gibt es keine Klassen, ausser formal. Wenn Du sinnvolle Klassen schreiben würdest, dann wüsstest Du wofür Klassen da sind, wie die funktionieren, was Vererbung ist, und dann auch was der Unterschied zwischen dem Aufruf einer Klassenmethode/statischen ”Methode” und dem Aufruf einer echten Methode ist. Um Deine Frage zu beantworten, müsstest Du die Grundlagen von objektorientierter Programmierung mit Klassen lernen.

Beide `machwasanderes()` ”Methoden” sind falsch weil in der ersten das `self` nicht verwendet wird, das also gar keine Methode ist, und in der zweiten kein `self` übergeben wird, was ich als Fehler ansehen würde wenn das nicht als `staticmethod` dekoriert ist. Die meisten IDEs werden Dir das wohl auch als Fehler anzeigen.

Die `__init__()`-Methoden sind mindestens mal überflüssig, weil die nichts machen. Genau das macht die `__init__()` von `object` nämlich auch schon.

”Richtig” wäre folgendes, wobei das nur richtig ist wenn man eine gute Begründung dafür hätte warum `machwasanderes()` eine statische Methode sein soll und nicht einfach eine Funktion:

Code: Alles auswählen

class Test:
    def machwas(self, anderes_argument):
        return self.machwasanderes(anderes_argument)

    @staticmethod
    def machwasanderes(noch_ein_argument):
        return 2 * noch_ein_argument
Was halt grundsätzlich falsch ist, ist dass diese ”Klasse” keinen Zustand modelliert und das `self` in der `machwas()` ”Methode” nicht wirklich verwendet wird, denn man könnte das problemlos mit zwei Funktionen schreiben, ohne die unnötige Klasse.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Gelöscht0903

@__blackjack__: Um es mal mit Rezo zu sagen: "Es ist mal wieder Zeit für so ein posting..."
Deine Antworte strotzen leider vor (negativen) Unterstellungen "Wenn Du mal xxx machen würdest, würdest Du verstehen..." Sowas ist weder mir positiv zugewandt, geschweige denn wertschätzend.

Dann schreibst Du: "Um Deine Frage zu beantworten, müsstest Du die Grundlagen von objektorientierter Programmierung mit Klassen lernen."

Tja, dann hab ich wohl verloren und verneige mich hiermit in schamvoller Demut vor dem objektorientierten Klassengott, der an mir seine geheiligte Zeit in hingebungsvoller Arroganz verschwendet hat.

Gehab Dich wohl!
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Du schreibst Klassen, also musst Du so oder so die Grundlagen davon lernen. Und wenn Du das machst, löst sich Deine Frage so ziemlich automatisch auf. Oder anders gesagt, müsste man Dir jetzt Klassen erklären, was a) nicht mal eben so in einem Beitrag gemacht ist, und b) schon an anderen Stellen zu Hauf zu finden ist.

Und dann musst Du sehr wahrscheinlich einiges an Deinem Programm um-/neuschreiben wenn Du da jetzt Funktionen sinnloserweise in Klassen gestopft hast. Wie man das dreht und wendet, da kommt Arbeit auf Dich zu, die Dir keiner abnehmen kann. Das kannst Du nicht mögen und willst Du vielleicht nicht hören, das ändert aber nichts.

Man kann an den kaputten Klassenbeispielen Deine Frage auch nicht wirklich beantworten, denn das sind ja gar keine richtigen Klassen. Mit denen kannst Du die Probleme gar nicht bekommen nach denen Du fragst. Denn das sind einfach nur Funktionen. Nimm die ”Klasse” weg, dann stellt sich die Frage nicht mehr. Problem gelöst.

Konstruktiver Rat: Beseitige alle Klassen aus Deinem Programm die gar keine sind. Das macht das Programm einfacher und verständlicher. Klassen sind kein Selbstzweck, die erfüllen eine Aufgabe. Wenn sie das nicht tun, ist das zusätzlicher Code und Komplexität die nichts bewirkt. Wenn man anfängt, ist es keine schlechte Idee erst einmal auf Klassen zu verzichten bis man zusammengehörende Daten identifiziert hat, die man zu einer Klasse zusammenfassen kann. Und dann kann man schauen ob es Sinn macht Operationen direkt auf so einem Objekt zur Verfügung zu stellen. Die greifen dann ja zwangsläufig auf `self` zu und man hat das Problem von Funktionen die ”Methoden” sind, nicht (so leicht).
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

In diesem Forum legt man großen Wert darauf, dass der Fragesteller "nachweist", dass er sich intensiv mit dem Thema beschäftigt hat. Eine Frage so zu nehmen, wie sie ist, fällt schwer. Ich kenne das aus meinen aktiveren Forumzeiten (damals mit Delphi) etwas anders. Dort hat man unterstellt, dass der Fragesteller wusste, was er tat. Erst, wenn er nachhaltig Unfug gemacht hat, wurde er weiterverwiesen. Hier wird eher unterstellt, dass er grundsätzlich erst mal keine Ahnung hat. Mag an der Programmiersprache python liegen, die m.E. einfach aussieht und kompliziert ist. Schon die Änderungen weniger Zeichen verändern die Wirkungsweise von Dingen ganz erheblich.
Aber ich möchte mich hier eben nicht an der Grundsatzdiskussion beteiligen.

Kürzeste Antwort:
Wenn Du Dich mit Objektorientierung beschäftigst und den Unterschied zwischen Klasse vs. Instanz bzw. Klassenvariable (bzw. Klassenfunktion) vs. Membervariable (bzw. Memberfunktion) verstehst, dann wird Dir das klar.

Etwas längere Antwort:
  • Eine Klassenvariable gibt es nur genau einmal pro Klasse, selbst, wenn da kein einziger Member existiert.
  • Eine Membervariable existiert für jede einzelne Instanz. Unterschiedliche Instanzen der selben Klasse können unterschiedliche Werte haben
Daraus abgeleitet das selbe für Funktionen:
Wenn eine Funktion auf Member- Variablen zugreift, dann kann das nicht als Klassenfunktion implementiert werden. Sie wüsste ja nicht, wo sie hingreifen soll. Es kann sein, dass es keinen oder mehrere Member gibt.

Nicht unerwähnt bleiben soll an dieser Stelle, dass es natürlich auch lokale/globale Variablen/Funktionen gibt.
Zwischen diesen Konzepten gilt es, vernünftig zu wählen.

Längste Anwort für heute:
Da Du nach Klassen/Instanzen gefragt hast, werde ich mal den Teil lokal/global nicht weiter auswalzen.

Als Klassenvariable könnte beispielsweise ein Instanz- Zähler dienen. Sobald eine Instanz der Klasse erzeugt wurde, muss dieser Zähler hochgezählt werden, wenn ein Objekt verschwindet, dann wieder heruntergezählt. (In Python ist letzteres nicht so ohne weiteres möglich, aber vom Konzept wäre es so). Man könnte auch in einer Klassenvariable irgendeinen Wert speichern, der definitiv für die gesamte Laufzeit des Programms gilt, aber umständlich beschafft werden muss (Anmeldedaten vielleicht).
Als Klassenfunktion werden typischerweise Funktionen verwendet, die irgendwas machen, was für ALLE Member der Klasse gilt (bzw. gelten würde).

Bei Klassenfunktionen/Klassenvariablen ist die Grenze zu (modul-)globalen Funktionen/Variablen fließend. Wenn sie mit der Klasse zu tun haben, dann sollten sie m.E. aus Gründen der Ordnung als Klassenfunktionen ausgebildet werden.

Als Membervariable dient eine Variable, wenn sie den Zustand eines Members beschreibt. Ein Instanz- Zähler wäre logischerweise bedeutungslos für den Zustand des Members selbst. Wenn das Objekt aber beispielsweise Fahrzeuge abbilden soll, dann muss schon jedes Fahrzeug sein eigenes Nummernschild kennen. Gleiches gilt dann für die Member- Funktionen. Wenn beispielsweise das Kennzeichen und das Baujahr als Zeichenkette ausgegeben werden soll, dann kann das nur eine Memberfunktion sein.

Einiges von dem, was ich gesagt habe, kann man natürlich auch anders erklären.

Ich versuche es auch mal abstrakt mit Pseudocode:

Code: Alles auswählen

class A:
    #Klassenvariable
    kv_letze_instanz : int = 0

    #Klassenfunktion
    def kf_baue_instanzen(anzahl_instanzen : int, init__wert : str):
        result = []
        for i in range(anzahl_instanzen):
            #Zugriff auf Klassenvariable
            A.kv_letze_instanz += 1
            instanz = A(id = A.kv_letze_instanz, dummy = init__wert)
            result.append(instanz)
        return result

    #Initialiserungsfunktion, diese wird immer implizit aufgerufen, wenn ein Objekt gebaut wird
    #der erste Parameter "self" steht für die Instanz/Member
    def __init__(self, id ; int, dummy : str):
        self.id, self.dummy = id, dummy

    #weitere Member- Funktion, hier wird auch auf die Klassenvariable zugegriffen
    def mach_was(self, irgendwas : str):
        self.dummy = irgendwas + self.dummy + ' LI:' +str (A.kv_letze_instanz)

#Benutzung der Klassen- Funktion
elemente = A.kf_baue_instanzen(5, 'X')

#Benutzung der Member- Objekte
for e in elemente:
    #Benutzung der Memberfunktionen
    e.mach_was('B->')
    print (f"ID {e.id} DUMMY {e.dummy}")
Das Ergebnis sieht dann wie folgt aus:

Code: Alles auswählen

ID 1 DUMMY B->X LI:5
ID 2 DUMMY B->X LI:5
ID 3 DUMMY B->X LI:5
ID 4 DUMMY B->X LI:5
ID 5 DUMMY B->X LI:5
Grundsätzlich würde ich vereinfachend folgendes postulieren: Nahezu IMMER willst Du Membervariablen und Memberfunktionen benutzen. Wenn Du Klassenvariablen und Klassenfunktionen benutzen willst, dann ist das etwas Besonderes.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@NoPy: Bei dir scheint einiges in der Nomenklatur durcheinander zu sein: Instanz und Member(-Objekt) sind keine Synonyme; und in Python ist der Begriff Member(-Variable) eher ungebräuchlich, man sagt da „Attribut“ zu. Genauso Memberfunktion → Methode.

Und `kf_baue_instanzen`, was du als Klassenfunktion bezeichnest, ist (wieder) eine kaputte Methode. Klassenmethoden müssen mit `@classmethod` erstellt werden, sonst sind es keine Klassenmethoden.
NoPy hat geschrieben: Donnerstag 8. Juli 2021, 16:14 Schon die Änderungen weniger Zeichen verändern die Wirkungsweise von Dingen ganz erheblich.
In welcher Programmiersprache ist das denn nicht so? Das hat das Programmieren IMHO so an sich, dass man genau arbeiten muss. Weil der Computer eben nicht mitdenkt.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

narpfel hat geschrieben: Donnerstag 8. Juli 2021, 17:08 @NoPy: Bei dir scheint einiges in der Nomenklatur durcheinander zu sein: Instanz und Member(-Objekt) sind keine Synonyme; und in Python ist der Begriff Member(-Variable) eher ungebräuchlich, man sagt da „Attribut“ zu. Genauso Memberfunktion → Methode.
Jupp, Du hast recht. War dumm. Hab ich lange nicht mehr erklärt. Attribut vs. Membervariable ist halt dem geschuldet, das Python eine m.E. etwas eigene Auffassung von Objektorientierung hat.
narpfel hat geschrieben: Donnerstag 8. Juli 2021, 17:08 Und `kf_baue_instanzen`, was du als Klassenfunktion bezeichnest, ist (wieder) eine kaputte Methode. Klassenmethoden müssen mit `@classmethod` erstellt werden, sonst sind es keine Klassenmethoden.
Was ist sie dann? Sie funktioniert, wie eine Klassenmethode. Sicher, könnte man auch aus der Klasse rausziehen, aber so viele leicht einleuchtende Beispiele für Klassenmethoden gibt es leider nicht.
Also wenn es eine "kaputte" Methode ist, dann sollte sie doch IMHO gegen Syntaxregeln verstoßen. Wenn sie das nicht tut, dann ist sie m.E. zulässig. Und wenn sie zulässig ist: Was genau unterscheidet sie nun von einer Klassenmethode?
narpfel hat geschrieben: Donnerstag 8. Juli 2021, 17:08
NoPy hat geschrieben: Donnerstag 8. Juli 2021, 16:14 Schon die Änderungen weniger Zeichen verändern die Wirkungsweise von Dingen ganz erheblich.
In welcher Programmiersprache ist das denn nicht so? Das hat das Programmieren IMHO so an sich, dass man genau arbeiten muss. Weil der Computer eben nicht mitdenkt.
Das ist schon richtig, aber da gibt es schon Abstufungen. Beispiele:

Code: Alles auswählen

#Einrückungen sind in nahezu allen Programmiersprachen bedeutungsfrei, von ABAP, COBOL und einigen Dinosauriern abgesehen
def irgendwas():
    summe = 0
    for i in range(10):
        summe += 1
       print (summe)
    #allein das Einrücken verändert die Bedeutung. Ich weiß, das ist bei python gewollt. Beim Verwenden von Code- Snippets mit Copy- Paste muss man gut aufpassen
    print (summe)
    #hier wäre eine aus Versehen falsche Einrückung fatal
    return summe

def was_anderes(self, x):
    #wenn a nicht definiert ist, dann wird es hier definiert
    a = x
    #falls das gemeint war, dann wundert man sich
    self.a = x
    #die meisten Programmiersprachen verlangen oder ermöglichen eine vorherige Deklaration. Dann kann der Compiler helfen, sonst nicht

a = irgendwas() #hier wird ein Funktionswert übergeben
b = irgendwas # hier die Funktion selbst. Das ist mindestens in Pascal unbedeutend. 

c = (1) # hier ist es eine Zahl
d = (1,) # hier ist es ein Tupel, diese Konstruktion gibt es idR in aderen Sprachen nicht. Ein Compiler meldet : Geht nicht
- Early Binding macht die Sprache sicher unflexibler, aber dafür gibt es mehr Compilerunterstützung.
- duck typing macht die Sprache flexibler, stellt aber auch höhere Ansprüche an Fehlervermeidung.

Das sind nur ein paar Beispiele für maximalen Häh- Effekt, die mir einfallen. Ich weiß, das ist Absicht und zählt als Errungenschaft. Aber auch bei 100 Vorteilen hat es eben auch ein oder zwei klitzekleine Nachteile.

UNd schon bin ich doch wieder in die Grundsatzdiskussion hineingeschlittert. Versprochen, mein letzter Beitrag in diesem Thread zum Grundsatz. Aber falls Du mir die Frage noch beantworten kannst, was die Funktion kf von einer Klassen- Funktion unterscheidet, wäre ich Dir dankbar:

Code: Alles auswählen

class A():
    kv = 0
    def kf():
        A.kv += 1
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Klassenmethoden werden oft dafür verwendet, um Alternative Initialisierungsmethoden anbieten zu können.

Auch wenn es hier schon mehrfach angesprochen worden ist, es gibt drei Arten von Methoden, die in einem Klassennamensraum definiert werden können. (Instanz)methoden, Klassenmethoden, und Statischemethoden:

Code: Alles auswählen

class A:
    def methode(self):
        # braucht self
        pass

    @classmethod
    def methode(cls):
        # braucht cls
        pass

    @staticmethod
    def methode():
        # braucht einen guten Grund, warum das unbedingt innerhalb der Klasse definiert wird
        pass
Eine Methode, die nicht in eine der drei Varianten fällt, ist ein Programmierfehler, auch wenn das kein Syntaxfehler ist, aber nicht alles was syntaktisch richtig ist, ist auch logisch richtig.
Für Deinen Fall, wäre classmethod das richtige:

Code: Alles auswählen

class A():
    kv = 0

    @classmethod
    def kf(cls):
        cls.kv += 1
Außer man hat Vererbung, dann müßte man doch direkt über A.kv gehen. Aber da Klassenvariablen mit globalen Variablen gleichzusetzen sind, und daher vermieden werden sollten, tritt so ein Fall hoffentlich nie auf.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Du kannst 'kf' auf einer Instanz von A nicht aufrufen. Damit wären wir wieder bei dem Kern des Problems, auf das /me bereits hingewiesen hat.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bin ehrlich gesagt ueberrascht, dass dieses Konstrukt eine Klassenmethode ohne @classmethod ueberhaupt funktioniert. Ich haette erwartet, dass es einen Fehler gibt. In Python2 tat es das:

Code: Alles auswählen

    elemente = A.kf_baue_instanzen(5, 'X')
TypeError: unbound method kf_baue_instanzen() must be called with A instance as first argument (got int instance instead)
Warum das nicht mehr so ist, muesste ich erstmal recherchieren. Aber einen klaren Nachteil deines Vorgehens, statt einer echten @classmethod, ist der Unterschied im Aufruf via Klasse und Instanz.

Baut man in dein Beispiel das hier ein

Code: Alles auswählen

#Benutzung der Member- Objekte
for e in elemente:
    #Benutzung der Memberfunktionen
    e.mach_was('B->')
    e.kf_baue_instanzen("foobar")
dann bekommt man einen Fehler. Denn das erste Argument ist nun notwendigerweise die Instanz von A, die gerade an e gebunden ist, und damit kann man die Methode nicht mehr aufrufen. Mit @classmethod geht es:

Code: Alles auswählen

class A:

    kv_letze_instanz = 0

    @classmethod
    def kf_baue_instanzen(cls, anzahl_instanzen, init__wert):
        result = []
        for i in range(anzahl_instanzen):
            A.kv_letze_instanz += 1
            instanz = A(id=A.kv_letze_instanz, dummy=init__wert)
            result.append(instanz)
        return result

    def __init__(self, id, dummy):
        self.id, self.dummy = id, dummy

    #weitere Member- Funktion, hier wird auch auf die Klassenvariable zugegriffen
    def mach_was(self, irgendwas):
        self.dummy = irgendwas + self.dummy + ' LI:' + str(A.kv_letze_instanz)


#Benutzung der Klassen- Funktion
print(A.kf_baue_instanzen)
elemente = A.kf_baue_instanzen(5, 'X')

#Benutzung der Member- Objekte
for e in elemente:
    #Benutzung der Memberfunktionen
    e.mach_was('B->')
    e.kf_baue_instanzen(1, "foobar")
Und persoenlich finde ich es eine Regression, dass Python 3 das ploetzlich erlaubt.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nur mal als Nachklapp: https://stackoverflow.com/questions/528 ... s/52832090 - es hat sich wohl einfach geaendert. Und SO hat natuerlich recht: eigentlich haben wir hier eine staticmethod vorliegen.

Ich find's immer noch doof, aber es ist halt so.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

kbr hat geschrieben: Freitag 9. Juli 2021, 10:34 Du kannst 'kf' auf einer Instanz von A nicht aufrufen. Damit wären wir wieder bei dem Kern des Problems, auf das /me bereits hingewiesen hat.
Wie man es nimmt, ich kann es und es funktioniert, wie erwartet. Wahrscheinlich, eben weil die Klasse als globale Variable existiert.
Ich vermute mal, dass das dann bei Vererbung schief geht.

Also danke für die Erleuchtung :D Wenn ich in Zukunft also mal eine Klassenfunktion brauchen sollte, dann mache ich es mit cls :)
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Auf der Klasse geht es, aber auf der Instanz geht es nicht. Dafür müsstest Du eine 'staticmethod' draus machen.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__deets__ hat geschrieben: Freitag 9. Juli 2021, 11:27 Nur mal als Nachklapp: https://stackoverflow.com/questions/528 ... s/52832090 - es hat sich wohl einfach geaendert. Und SO hat natuerlich recht: eigentlich haben wir hier eine staticmethod vorliegen.

Ich find's immer noch doof, aber es ist halt so.
Naja, man könnte argumentieren, daß die Sprache dadurch konsistenter wird. Wenn man Funktionen frei innerhalb einer anderen Funktion deklarieren kann, warum soll das nicht auch innerhalb einer Klasse gehen? Andererseits sorgt das wieder für neue Seltsamkeiten: auf einer Instanz aufgerufen, verhält sich so eine Funktion wie eine classmethod, und auf der Klasse aufgerufen, verhält es sich wie eine staticmethod.
Antworten