Forward Declarations

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
gorba
User
Beiträge: 100
Registriert: Freitag 28. Juli 2006, 14:58

Kann ich meine Funktionen deklarieren, so, dass ich sozusagen die Funktion bekannt gebe, sie aber erst weiter unten im Programm auscodieren kann?
BlackJack

Das Problem gibt es in Python nicht. Alle Namen werden zur Laufzeit aufgelöst. Es gibt keine Deklarationen, ausser ``global`` aber das ist eine andere Baustelle.

Code: Alles auswählen

def spam():
    return ham()

def ham():
    return 42
Das funktioniert ohne Probleme weil `ham` erst aufgelöst wird, wenn `spam()` tatsächlich ausgeführt wird.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Code: Alles auswählen

def spam():
    return ham()

print spam()

def ham():
    return 42
Das würde dann aber nicht gehen, oder?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Nein.
BlackJack

CrackPod hat geschrieben:

Code: Alles auswählen

def spam():
    return ham()

print spam()

def ham():
    return 42
Das würde dann aber nicht gehen, oder?
Nein. Falls das jemand erwarten würde, dann liegt das vielleicht daran, dass er ``def`` fälschlicherweise als Deklaration ansieht. ``def`` ist aber eine ausführbare Anweisung wie ``print``. Hier wird nach dem kompilieren erst das ``def spam():`` ausgeführt und dann das ``print spam()``. Zu dem Zeitpunkt wurde noch nichts ausgeführt, was den Namen `ham` an ein aufrufbares Objekt bindet, darum gibt's einen `NameError`.
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Nimm klassen, da gibts das problem garnicht erst.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Mad-Marty hat geschrieben:Nimm klassen, da gibts das problem garnicht erst.
Das ist aber auch keine bessere Lösung. Nicht jedes Problem braucht eine Klasse (gottseidank)!
Zuletzt geändert von birkenfeld am Sonntag 6. August 2006, 22:46, insgesamt 1-mal geändert.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo,

bei Klassen kann man auf alle im Körper der Klasse definierten Funktionsnamen unter Verwendung der automatisch übergebenen eigenen Objektreferenz zugreifen.

Code: Alles auswählen

class dummy:
    def spam(self):
        return self.ham()

    def ausgabe(self):
        print self.spam()

    def ham(self):
        return 42

dummy().ausgabe()
Eventuell ist das aber nicht der Kern der eigentlichen Frage. Vielleicht möchte gorba die Funktion als Argument übergeben und den Funktionscode später erst definieren.

Wenn das der Fall ist, sehe ich spontan zwei Möglichkeiten. Die schwierige ist, den neuen Code mit dem builtin Befehl "compile" zu erzeugen und dann dem Funktionsattribut func_code zuzuweisen. Ganz firm bin ich darin aber nicht und würde das mit Vorsicht genießen.

Die zweite einfache Variante ist wie immer ein Workaround: man definiert eine Funktion, die ein Element eines globalen Objektes ruft. :D

Code: Alles auswählen

def dummy(s): print "Guter", s              ##  erste Deklaration
dFunktionen = { "ausgabe": dummy }   ##  in globales Objekt aufnehmen

def ausgabe(s): dFunktionen["ausgabe"](s)   ##  aktuell referenzierte Funktion ausfuehren

ausgabe("Bulle")   ##  Testausgabe

def dummy(s): print "Boeser", s        ##  Neudeklaration
dFunktionen["ausgabe"] = dummy    ##   uebergabe an globales Objekt

ausgabe("Bulle")                   ##  gleiches Argument an gleiche Funktion, aber ohoo...
Ich hoffe das erklärt sich von selbst. :-)

Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Moinmoin,

die letzte Erklärung mit dem Dictionary wirkt etwas grobschlächtig, wenn es nur um eine Funktion geht, hat aber Vorteile, wenn mehrere zusammengefasst werden können. Es ist übrigens nicht notwendig, die Funktion vorher zu definieren, wie man in der folgenden Gegenüberstellung sehen kann.

Code: Alles auswählen

#######################
##  funktioniert nicht!
def ausgabe(a): print "denkste"     ##  Funktion mit Code definieren
funk = ausgabe                      ##  Namen an Callback-Variable geben
def ausgabe(a): print a             ##  Funktionscode aendern
funk("Hallo Welt!")                 ##  welche Funktion wird ausgefuehrt?

#######################
##  so geht es
f = None                            ##  Funktionsnamen initialisieren
def ausgabe(a): f(a)                ##  Wrapperfunktion ruft Funktion (Argumente!)
funk = ausgabe                      ##  Namen an Callback-Variable geben
def f(a): print a                   ##  Funktion ausformulieren
funk("Hallo Welt!")                 ##  jetzt wird die aktualisierte Funkt. gerufen
Dieser direkte Vergleich zeigt, dass man nicht den Funktionsnamen der Zielfunktion übergeben darf, sondern eine Wrapper-Funktion schaffen muss, die auf die globale Variable verweist. In diesem Zusammenhang hüte man sich vor einer Deklaration wie

Code: Alles auswählen

def ausgabe(a, f=f)
in Zeile 11. Denn durch f=f wird eine neue Variable im lokalen Namensraum geschaffen und der Bezug geht, wie in der oberen Variante, verloren. Solange man "f" in "ausgabe" nicht überschreibt, verweist es auf den globalen Namensraum.

Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Michael Schneider hat geschrieben:bei Klassen kann man auf alle im Körper der Klasse definierten Funktionsnamen unter Verwendung der automatisch übergebenen eigenen Objektreferenz zugreifen.

Code: Alles auswählen

class dummy:
    def spam(self):
        return self.ham()

    def ausgabe(self):
        print self.spam()

    def ham(self):
        return 42

dummy().ausgabe()
Und welches "Problem" löst das jetzt genau? Ohne die Klasse, mit den Methoden als Funktionen auf Modulebene funktioniert's genau so, nur das man sich die überflüssige Klasse spart:

Code: Alles auswählen

def spam():
    return ham()

def ausgabe():
    print spam()

def ham():
    return 42

ausgabe()
Andererseits gibt's auch bei Klassendefinitionen einen `NameError` wenn bei der Ausführung auf einen Namen zugegriffen wird, der noch nicht gebunden ist:

Code: Alles auswählen

class A:
    load = staticmethod(load)
    def load(self, filename):
        pass

a = A()
Das ``def load()`` muss halt ausgeführt werden bevor man auf `load` lesend zugreifen.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi BlackJack,

Du hast recht, aber auch wenn das auf Modulebene geht, widerspricht es nicht meiner Aussage:
Michael Schneider hat geschrieben: bei Klassen kann man auf alle im Körper der Klasse definierten Funktionsnamen unter Verwendung der automatisch übergebenen eigenen Objektreferenz zugreifen.
Das jedoch schon :-):
BlackJack hat geschrieben:

Code: Alles auswählen

class A:
    load = staticmethod(load)
    def load(self, filename):
        pass

a = A()
In diesem Fall bringt die Klasse keinen Vorteil und ich würde die Funktionen auch vorziehen. Aber in vielen (komplexeren) Fällen ist die strukturelle Kapselung in Klassen eben übersichtlicher. Muss man von Fall zu Fall entscheiden.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Antworten