È possibile dichiarare in avanti una funzione in Python?


189

È possibile dichiarare in avanti una funzione in Python? Voglio ordinare un elenco usando la mia cmpfunzione prima che venga dichiarato.

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

Ho organizzato il mio codice per inserire la definizione del cmp_configsmetodo dopo l'invocazione. Non riesce con questo errore:

NameError: name 'cmp_configs' is not defined

C'è un modo per "dichiarare" cmp_configs metodo prima che venga utilizzato? Renderebbe il mio codice più pulito?

Presumo che alcune persone saranno tentate di dirmi che dovrei semplicemente riorganizzare il mio codice in modo da non avere questo problema. Tuttavia, ci sono casi in cui questo è probabilmente inevitabile, ad esempio quando si implementano alcune forme di ricorsione. Se non ti piace questo esempio, supponi che io abbia un caso in cui è davvero necessario inoltrare una funzione.

Considera questo caso in cui in Python sarebbe necessaria la dichiarazione in avanti di una funzione:

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

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

Dove end_conditione end_resultsono stati precedentemente definiti.

È l'unica soluzione per riorganizzare il codice e mettere sempre le definizioni prima delle invocazioni?

Risposte:


76

Se non si desidera definire una funzione prima che venga utilizzata e definirla successivamente è impossibile, che ne dici di definirla in qualche altro modulo?

Tecnicamente lo definisci ancora per primo, ma è pulito.

È possibile creare una ricorsione come la seguente:

def foo():
    bar()

def bar():
    foo()

Le funzioni di Python sono anonime proprio come i valori sono anonimi, ma possono essere associati a un nome.

Nel codice sopra, foo()non chiama una funzione con il nome pippo, chiama una funzione che sembra essere legata al nome foonel punto in cui viene effettuata la chiamata. È possibile ridefinire fooaltrove e barquindi chiamare la nuova funzione.

Il tuo problema non può essere risolto perché è come chiedere di ottenere una variabile che non è stata dichiarata.


47
in breve, se hai __name__ == '__main__': main () come ultima riga del tuo script, tutto andrà bene!
Filipe Pina,

3
@FilipePina Non ho capito il tuo commento - perché non riesci a mettere l'ultima riga del codice semplicemente main()?
Sanjay Manohar,

11
@SanjayManohar: per evitare di eseguirlo ilimport your_module
jfs

2
Vorrei aggiungere - a volte è possibile aggirare questi problemi usando lambda poiché vengono valutati in seguito.
Joe,

2
Scritto "anonimo", intendi davvero "oggetti di prima classe".
danielm,

119

Quello che puoi fare è avvolgere l'invocazione in una sua funzione.

Così che

foo()

def foo():
    print "Hi!"

si romperà, ma

def bar():
    foo()

def foo():
    print "Hi!"

bar()

funzionerà correttamente.

La regola generale in nonPython è che la funzione debba essere definita più in alto nel codice (come inPascal ), ma che dovrebbe essere definita prima del suo utilizzo.

Spero che aiuti.


20
+1 risposta più diretta, con il concetto chiave di volta: Pascal = definisci più in alto, Python = definisci prima.
Bob Stein,

1
Questa è la risposta corretta, spiega anche perché la if __name__=="__main__":soluzione funziona.
00prometheus,

2
Se capisco correttamente, significa che l'esempio spam () e eggs () di OP va bene come scritto. È corretto?
Krubo,

1
@krubo sì, va bene come scritto
lxop

92

Se avvii il tuo script in modo da avviare quanto segue:

if __name__=="__main__":
   main()

quindi probabilmente non devi preoccuparti di cose come "dichiarazione anticipata". Vedete, l'interprete dovrebbe caricare tutte le funzioni e quindi avviare la funzione main (). Certo, assicurati di aver corretto anche tutte le importazioni ;-)

Vieni a pensarci bene, non ho mai sentito una cosa come "dichiarazione anticipata" in Python ... ma poi di nuovo, potrei sbagliarmi ;-)


14
+1 risposta più pratica: se metti questo codice in fondo al file sorgente più esterno, sei libero di definirlo in qualsiasi ordine.
Bob Stein,

2
Ottimo consiglio; mi aiuta davvero; poiché preferisco di più la programmazione "top-down" rispetto al bottom-up.
GhostCat,

10

Se la chiamata a cmp_configs è all'interno della propria definizione di funzione, si dovrebbe andare bene. Faccio un esempio.

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.

In generale, inserire il codice all'interno di funzioni (come main ()) risolverà il problema; chiama main () alla fine del file.


10

Mi scuso per aver rilanciato questo thread, ma c'era una strategia non discussa qui che potrebbe essere applicabile.

Usando la riflessione è possibile fare qualcosa di simile per inoltrare la dichiarazione. Ad esempio, supponiamo che tu abbia una sezione di codice simile a questa:

# 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]()

Quindi in questo modo abbiamo determinato quale funzione vogliamo chiamare prima che sia effettivamente definita, effettivamente una dichiarazione a termine. In Python l'istruzione globals()[function_name]()è lo stesso che foo()se function_name = 'foo'per le ragioni discusse in precedenza, dal momento che deve pitone occhiata ogni funzione prima di chiamare. Se uno dovesse usare il timeitmodulo per vedere come si confrontano queste due affermazioni, esse hanno lo stesso costo computazionale.

Naturalmente l'esempio qui è molto inutile, ma se si dovesse avere una struttura complessa che doveva eseguire una funzione, ma deve essere dichiarata prima (o strutturalmente ha poco senso averla dopo), si può semplicemente memorizzare una stringa e prova a chiamare la funzione più tardi.


9

In Python non esiste nulla di simile alla dichiarazione diretta. Devi solo assicurarti che la tua funzione sia dichiarata prima che sia necessaria. Si noti che il corpo di una funzione non viene interpretato fino a quando la funzione non viene eseguita.

Considera il seguente esempio:

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.

Puoi pensare che un corpo di una funzione sia solo un altro script che verrà interpretato una volta chiamata la funzione.


7

No, non credo che ci sia modo di dichiarare in avanti una funzione in Python.

Immagina di essere l'interprete Python. Quando arrivi alla linea

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

o sai cos'è cmp_configs o no. Per procedere, devi conoscere cmp_configs. Non importa se c'è ricorsione.


9
bene questo è se stai facendo solo un passaggio sul codice. Alcuni compilatori (e mi rendo conto che Python è interpretato) fanno due passaggi, in modo che queste cose possano essere capite. la dichiarazione a termine, o almeno una sorta di scoperta mirata, sarebbe davvero buona.
Mark Lakewood,

7

A volte un algoritmo è più semplice da comprendere dall'alto verso il basso, a partire dalla struttura generale e dal dettaglio nei dettagli.

Puoi farlo senza dichiarazioni a termine:

def main():
  make_omelet()
  eat()

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

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

# ...

main()

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()

Produzione:

Hello, world!

3

Non è possibile dichiarare in avanti una funzione in Python. Se hai l'esecuzione della logica prima di aver definito le funzioni, probabilmente hai comunque un problema. Metti la tua azione in unif __name__ == '__main__' a alla fine del tuo script (eseguendo una funzione che chiami "main" se non è banale) e il tuo codice sarà più modulare e sarai in grado di usarlo come modulo se ne hai bisogno per.

Inoltre, sostituisci la comprensione di tale elenco con un generatore express (ovvero, print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Inoltre, non utilizzare cmp, che è deprecato. Utilizzare keye fornire una funzione di meno di.


Come posso fornire una funzione inferiore a?
Nathan Fellman,

Invece di cmp_configs, definiresti una funzione che accetta due argomenti e restituisce True se il primo è inferiore al secondo e False altrimenti.
Mike Graham,

Per quelli di noi che provengono da uno sfondo di tipo C, non c'è nulla di irragionevole nell'esecuzione della logica prima che le funzioni siano definite. Pensa: "compilatore multi-pass". A volte ci vuole un po 'di tempo per adattarsi alle nuove lingue :)
Luca H

3

Importa il file stesso. Supponendo che il file si chiami test.py:

import test

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

1

"riorganizza il mio codice in modo da non avere questo problema." Corretta. Facile da fare. Funziona sempre

Puoi sempre fornire la funzione prima del suo riferimento.

"Tuttavia, ci sono casi in cui questo è probabilmente inevitabile, ad esempio quando si implementano alcune forme di ricorsione"

Non riesco a vedere come sia possibile anche da remoto. Fornire un esempio di un luogo in cui non è possibile definire la funzione prima del suo utilizzo.


Ho una situazione del genere. Sto cercando di passare i tipi in un decoratore di funzioni e i tipi sono definiti più in basso nel modulo. Non posso spostare in alto i tipi offensivi, perché ciò spezzerebbe la catena dell'eredità.
Joe,

L'ho risolto passando un lambda al mio decoratore invece del tipo reale; ma non saprei come risolverlo altrimenti (ciò non mi richiederebbe di riordinare le mie eredità)
Joe,

0

Adesso aspetta un minuto. Quando il tuo modulo raggiunge l'istruzione print nel tuo esempio, prima che cmp_configssia stato definito, che cosa ti aspetti che faccia esattamente?

Se la pubblicazione di una domanda utilizzando print sta davvero cercando di rappresentare qualcosa del genere:

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

quindi non è necessario definire cmp_configs prima di eseguire questa istruzione, basta definirla più avanti nel codice e tutto andrà bene.

Ora, se stai provando a fare riferimento cmp_configscome valore predefinito di un argomento alla lambda, allora questa è una storia diversa:

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

Ora hai bisogno di una cmp_configsvariabile definita prima di raggiungere questa linea.

[EDIT: questa parte successiva risulta non corretta, poiché il valore dell'argomento predefinito verrà assegnato quando viene compilata la funzione e tale valore verrà utilizzato anche se si modifica il valore di cmp_configs in seguito.]

Fortunatamente, essendo Python così accomodante, non importa cosa si definisce cmp_configs, quindi si può semplicemente prefigurare questa affermazione:

cmp_configs = None

E il compilatore sarà felice. Assicurati di dichiarare il reale cmp_configsprima di invocare fn.


-1

Un modo è creare una funzione gestore. Definisci il gestore in anticipo e metti il ​​gestore sotto tutti i metodi che devi chiamare.

Quindi, quando invochi il metodo del gestore per chiamare le tue funzioni, saranno sempre disponibili.

Il gestore potrebbe sostenere una discussione nameOfMethodToCall. Quindi utilizza una serie di istruzioni if ​​per chiamare il metodo giusto.

Ciò risolverebbe il tuo problema.

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)

questo sembra molto poco sincero. Python dovrebbe gestire questo tipo di cose da solo.
Nathan Fellman,

rileggere la risposta accettata. Python non ha bisogno di definire la funzione fino a quando non la chiami , non la usi solo in una definizione.
Tacaswell

-3

Sì, possiamo verificarlo.

Ingresso

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()

Produzione

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.

Come BJ Homer ha menzionato sopra i commenti sopra, Una regola generale in Python non è che la funzione dovrebbe essere definita più in alto nel codice (come in Pascal), ma che dovrebbe essere definita prima del suo utilizzo.

Spero che aiuti.


2
Non viene print_lyrics()chiamato (utilizzato) nella riga 1 prima di essere definito? Ho copiato questo pezzo di codice e ho provato a eseguirlo, e mi dà l'errore NameError: name 'print_lyrics' is not definedsulla riga 1. Puoi spiegarlo?
Bugs Buggy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.