Ist es möglich, eine Funktion in Python vorwärts zu deklarieren?

188

Ist es möglich, eine Funktion in Python vorwärts zu deklarieren? Ich möchte eine Liste mit meiner eigenen cmpFunktion sortieren, bevor sie deklariert wird.

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Ich habe meinen Code so organisiert, dass die Definition der cmp_configsMethode nach dem Aufruf eingefügt wird. Es schlägt mit diesem Fehler fehl:

NameError: name 'cmp_configs' is not defined

Gibt es eine Möglichkeit, die cmp_configsMethode zu "deklarieren" , bevor sie verwendet wird? Es würde meinen Code sauberer aussehen lassen?

Ich gehe davon aus, dass einige Leute versucht sein werden, mir zu sagen, dass ich meinen Code einfach neu organisieren sollte, damit ich dieses Problem nicht habe. Es gibt jedoch Fälle, in denen dies wahrscheinlich unvermeidbar ist, beispielsweise bei der Implementierung einiger Formen der Rekursion. Wenn Ihnen dieses Beispiel nicht gefällt, nehmen Sie an, dass ich einen Fall habe, in dem es wirklich notwendig ist, eine Funktion weiterzuleiten.

Stellen Sie sich diesen Fall vor, in dem eine Vorwärtsdeklaration einer Funktion in Python erforderlich wäre:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

Wo end_conditionund end_resultwurden zuvor definiert.

Ist die einzige Lösung, um den Code neu zu organisieren und Definitionen immer vor Aufrufen zu setzen?

Nathan Fellman
quelle

Antworten:

76

Wenn Sie nicht wollen , eine Funktion zu definieren , bevor es verwendet wird , und es definiert , danach ist unmöglich, was es in einem anderen Modul zu definieren?

Technisch definieren Sie es immer noch zuerst, aber es ist sauber.

Sie können eine Rekursion wie die folgende erstellen:

def foo():
    bar()

def bar():
    foo()

Pythons Funktionen sind anonym, genauso wie Werte anonym sind, sie können jedoch an einen Namen gebunden werden.

Ruft im obigen Code foo()keine Funktion mit dem Namen foo auf, sondern ruft eine Funktion fooauf, die zum Zeitpunkt des Aufrufs zufällig an den Namen gebunden ist. Es ist möglich, foowoanders neu zu definieren und bardann die neue Funktion aufzurufen.

Ihr Problem kann nicht gelöst werden, da Sie nach einer nicht deklarierten Variablen fragen möchten.

RichN
quelle
47
Kurz gesagt, wenn Sie if __name__ == '__main__': main () als letzte Zeile in Ihrem Skript haben, ist alles in Ordnung!
Filipe Pina
3
@FilipePina Ich habe Ihren Kommentar nicht verstanden - warum können Sie die letzte Zeile des Codes nicht so einfach setzen main()?
Sanjay Manohar
11
@ SanjayManohar: um zu vermeiden, dass es amimport your_module
jfs
2
Ich möchte hinzufügen - manchmal ist es möglich, diese Probleme mit Lambdas zu umgehen, da sie später ausgewertet werden.
Joe
2
Mit "anonym" meinen Sie wirklich "erstklassige Objekte".
Danielm
117

Sie können den Aufruf in eine eigene Funktion einbinden.

Damit

foo()

def foo():
    print "Hi!"

wird brechen, aber

def bar():
    foo()

def foo():
    print "Hi!"

bar()

wird richtig funktionieren.

Die allgemeine Regel in Pythonist nicht, dass die Funktion im Code höher definiert werden sollte (wie in Pascal), sondern dass sie vor ihrer Verwendung definiert werden sollte.

Hoffentlich hilft das.

Wanja
quelle
20
+1 direkteste Antwort mit dem Keystone-Konzept: Pascal = höher definieren, Python = früher definieren.
Bob Stein
1
Dies ist die richtige Antwort und erklärt auch, warum die if __name__=="__main__":Lösung funktioniert.
00prometheus
2
Wenn ich das richtig verstehe, bedeutet dies, dass das Beispiel für Spam () und Eier () von OP in Ordnung ist. Ist das korrekt?
Krubo
1
@krubo ja, es ist in Ordnung wie geschrieben
lxop
92

Wenn Sie Ihr Skript wie folgt starten:

if __name__=="__main__":
   main()

dann müssen Sie sich wahrscheinlich nicht um Dinge wie "Vorwärtsdeklaration" kümmern. Sie sehen, der Interpreter würde alle Ihre Funktionen laden und dann Ihre main () - Funktion starten. Stellen Sie natürlich sicher, dass auch alle Importe korrekt sind ;-)

Kommen Sie und denken Sie darüber nach, ich habe noch nie so etwas wie "Forward Declaration" in Python gehört ... aber andererseits könnte ich mich irren ;-)

jldupont
quelle
14
+1 praktischste Antwort: Wenn Sie diesen Code am Ende der äußersten Quelldatei einfügen , können Sie ihn in beliebiger Reihenfolge definieren.
Bob Stein
2
Toller Tipp; hilft mir wirklich; da ich viel lieber "von oben nach unten" programmiere als von unten nach oben.
GhostCat
10

Wenn sich der Aufruf von cmp_configs in einer eigenen Funktionsdefinition befindet, sollte dies in Ordnung sein. Ich werde ein Beispiel geben.

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

Im Allgemeinen wird Ihr Problem behoben, wenn Sie Ihren Code in Funktionen (wie main ()) einfügen. Rufen Sie einfach main () am Ende der Datei auf.

BJ Homer
quelle
10

Ich entschuldige mich für die Wiederbelebung dieses Threads, aber es gab eine Strategie, die hier nicht diskutiert wurde und möglicherweise anwendbar ist.

Mit Reflexion ist es möglich, etwas zu tun, das der Vorwärtsdeklaration ähnelt. Angenommen, Sie haben einen Codeabschnitt, der folgendermaßen aussieht:

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

Auf diese Weise haben wir festgelegt, welche Funktion wir aufrufen möchten, bevor sie tatsächlich definiert wird, effektiv eine Vorwärtsdeklaration. In Python die Aussage globals()[function_name]()ist das gleiche wie foo()bei function_name = 'foo'den oben erörterten Gründen, da Python jede Funktion vor dem Aufruf es Nachschlag muss. Wenn man das timeitModul verwendet, um zu sehen, wie diese beiden Aussagen verglichen werden, haben sie genau den gleichen Rechenaufwand.

Natürlich ist das Beispiel hier sehr nutzlos, aber wenn man eine komplexe Struktur hat, die eine Funktion ausführen muss, aber vorher deklariert werden muss (oder strukturell macht es wenig Sinn, sie danach zu haben), kann man einfach einen String und speichern Versuchen Sie später, die Funktion aufzurufen.

KGardevoir
quelle
8

In Python gibt es keine Vorwärtsdeklaration. Sie müssen nur sicherstellen, dass Ihre Funktion deklariert ist, bevor sie benötigt wird. Beachten Sie, dass der Hauptteil einer Funktion erst interpretiert wird, wenn die Funktion ausgeführt wird.

Betrachten Sie das folgende Beispiel:

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

Sie können sich vorstellen, dass ein Funktionskörper nur ein weiteres Skript ist, das interpretiert wird, sobald Sie die Funktion aufrufen.

Piotr Czapla
quelle
7

Nein, ich glaube nicht, dass es eine Möglichkeit gibt, eine Funktion in Python vorwärts zu deklarieren.

Stellen Sie sich vor, Sie sind der Python-Interpreter. Wenn Sie an die Linie kommen

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Entweder wissen Sie, was cmp_configs ist, oder Sie wissen es nicht. Um fortzufahren, müssen Sie cmp_configs kennen. Es spielt keine Rolle, ob es eine Rekursion gibt.

unutbu
quelle
9
Nun, dies ist, wenn Sie nur einen Durchgang über den Code machen. Einige Compiler (und mir ist klar, dass Python interpretiert wird) führen zwei Durchgänge durch, damit diese Dinge herausgefunden werden können. Eine Vorwärtserklärung oder zumindest eine Art Entdeckung mit Umfang wäre wirklich gut.
Mark Lakewood
7

Manchmal ist ein Algorithmus von oben nach unten am einfachsten zu verstehen, angefangen bei der Gesamtstruktur bis hin zu den Details.

Sie können dies ohne Vorwärtserklärungen tun:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for egg in carton:
    break(egg)

# ...

main()
Funroll
quelle
4
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

Ausgabe:

Hello, world!
jmurphy61
quelle
3

Sie können eine Funktion in Python nicht vorwärts deklarieren. Wenn Sie Logik ausführen lassen, bevor Sie Funktionen definiert haben, haben Sie wahrscheinlich sowieso ein Problem. Fügen Sie Ihre Aktion if __name__ == '__main__'am Ende Ihres Skripts ein (indem Sie eine Funktion ausführen, die Sie "main" nennen, wenn sie nicht trivial ist), und Ihr Code wird modularer und Sie können ihn bei Bedarf als Modul verwenden zu.

Ersetzen Sie dieses Listenverständnis auch durch einen Generator Express (dh print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Verwenden Sie auch nicht cmp, was veraltet ist. Verwenden keyund bieten Sie eine Funktion, die weniger als ist.

Mike Graham
quelle
Wie biete ich eine weniger als Funktion an?
Nathan Fellman
Anstelle von cmp_configs würden Sie eine Funktion definieren, die zwei Argumente akzeptiert und True zurückgibt, wenn das erste kleiner als das zweite ist, und andernfalls False.
Mike Graham
Für diejenigen von uns, die aus einem C-ähnlichen Hintergrund stammen, ist es nicht unangemessen, Logik auszuführen, bevor Funktionen definiert werden. Denken Sie: "Multi-Pass-Compiler". Manchmal dauert es eine Weile, um sich an die neuen Sprachen anzupassen :)
Luke H
3

Importieren Sie die Datei selbst. Angenommen, die Datei heißt test.py:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
user10488833
quelle
1

"Organisieren Sie einfach meinen Code neu, damit ich dieses Problem nicht habe." Richtig. Leicht zu schaffen. Funktioniert immer.

Sie können die Funktion immer vor ihrer Referenz bereitstellen.

"Es gibt jedoch Fälle, in denen dies wahrscheinlich unvermeidbar ist, beispielsweise bei der Implementierung einiger Formen der Rekursion."

Ich kann nicht sehen, wie das überhaupt aus der Ferne möglich ist. Bitte geben Sie ein Beispiel für einen Ort an, an dem Sie die Funktion vor ihrer Verwendung nicht definieren können.

S.Lott
quelle
Ich habe so eine Situation. Ich versuche, Typen in einem Funktionsdekorator zu übergeben, und die Typen werden weiter unten im Modul definiert. Ich kann die beleidigenden Typen nicht nach oben verschieben, da dies die Vererbungskette unterbrechen würde.
Joe
Ich habe es behoben, indem ich meinem Dekorateur anstelle des eigentlichen Typs ein Lambda übergeben habe. aber ich würde nicht wissen, wie ich es sonst beheben könnte (das würde nicht erfordern, dass ich meine Erbschaften neu ordne)
Joe
0

Jetzt warte eine Minute. cmp_configsWas genau erwarten Sie von Ihrem Modul, wenn es die print-Anweisung in Ihrem Beispiel erreicht hat , bevor es definiert wurde?

Wenn Ihre Veröffentlichung einer Frage mit print wirklich versucht, so etwas darzustellen:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

Dann müssen Sie diese nicht definieren, cmp_configsbevor Sie diese Anweisung ausführen. Definieren Sie sie einfach später im Code, und alles wird gut.

Wenn Sie nun versuchen, cmp_configsals Standardwert eines Arguments auf das Lambda zu verweisen , ist dies eine andere Geschichte:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Jetzt müssen Sie eine cmp_configsVariable definieren, bevor Sie diese Zeile erreichen.

[BEARBEITEN - Dieser nächste Teil stellt sich als nicht korrekt heraus, da der Standardargumentwert beim Kompilieren der Funktion zugewiesen wird und dieser Wert auch dann verwendet wird, wenn Sie den Wert von cmp_configs später ändern.]

Glücklicherweise ist es Python egal , als was er definiert, da er cmp_configsso typanpassend ist , also könnten Sie einfach mit dieser Aussage vorwegnehmen:

cmp_configs = None

Und der Compiler wird sich freuen. Stellen Sie nur sicher, dass Sie den Real deklarieren, cmp_configsbevor Sie ihn aufrufen fn.

PaulMcG
quelle
-1

Eine Möglichkeit besteht darin, eine Handlerfunktion zu erstellen. Definieren Sie den Handler frühzeitig und stellen Sie den Handler unter alle Methoden, die Sie aufrufen müssen.

Wenn Sie dann die Handler-Methode aufrufen, um Ihre Funktionen aufzurufen, sind sie immer verfügbar.

Der Handler könnte ein Argument annehmen nameOfMethodToCall. Verwendet dann eine Reihe von if-Anweisungen, um die richtige Methode aufzurufen.

Dies würde Ihr Problem lösen.

def foo():
    print("foo")
    #take input
    nextAction=input('What would you like to do next?:')
    return nextAction

def bar():
    print("bar")
    nextAction=input('What would you like to do next?:')
    return nextAction

def handler(action):
    if(action=="foo"):
        nextAction = foo()
    elif(action=="bar"):
        nextAction = bar()
    else:
        print("You entered invalid input, defaulting to bar")
        nextAction = "bar"
    return nextAction

nextAction=input('What would you like to do next?:')

while 1:
    nextAction = handler(nextAction)
fettleibig13
quelle
das scheint sehr unpythonisch. Python sollte diese Art von Sachen alleine erledigen.
Nathan Fellman
Lesen Sie die akzeptierte Antwort erneut. Python muss die Funktion erst definieren, wenn Sie sie aufrufen , und nicht nur in einer Definition verwenden.
Tacaswell
-3

Ja, wir können das überprüfen.

Eingang

print_lyrics() 
def print_lyrics():

    print("I'm a lumberjack, and I'm okay.")
    print("I sleep all night and I work all day.")

def repeat_lyrics():
    print_lyrics()
    print_lyrics()
repeat_lyrics()

Ausgabe

I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

Wie BJ Homer oben erwähnt hat, lautet eine allgemeine Regel in Python nicht, dass die Funktion im Code höher definiert werden sollte (wie in Pascal), sondern dass sie vor ihrer Verwendung definiert werden sollte.

Hoffentlich hilft das.

Satish Reddy Venkannagari
quelle
2
Wird print_lyrics()in Zeile 1 nicht aufgerufen (verwendet), bevor es definiert ist? Ich habe diesen Code kopiert und versucht, ihn auszuführen, und es wird der Fehler NameError: name 'print_lyrics' is not definedin Zeile 1 angezeigt. Können Sie das erklären?
Bugs Buggy