Este posibilă declararea directă a unei funcții în Python? (Programare, Python, Declarație De Înaintare)

Nathan Fellman a intrebat.

Este posibilă declararea directă a unei funcții în Python? Vreau să sortez o listă folosind propria mea cmp înainte ca aceasta să fie declarată.

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

Mi-am organizat codul pentru a pune definiția funcției cmp_configs metodei după invocare. Nu reușește cu această eroare:

NameError: name 'cmp_configs' is not defined

Există vreo modalitate de a „declara” cmp_configs metoda înainte de a fi utilizată? Ar face codul meu să arate mai curat?

Presupun că unele persoane vor fi tentate să-mi spună că ar trebui doar să-mi reorganizez codul astfel încât să nu am această problemă. Cu toate acestea, există cazuri în care acest lucru este probabil inevitabil, de exemplu, atunci când se implementează anumite forme de recursivitate. Dacă nu vă place acest exemplu, presupuneți că am un caz în care este într-adevăr necesar să declar în avans o funcție.

Luați în considerare acest caz în care ar fi necesară declararea în avans a unei funcții în Python:

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

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

Unde end_condition și end_result au fost definite anterior.

Este singura soluție să reorganizăm codul și să punem întotdeauna definițiile înaintea invocărilor?

17 răspunsuri
RichN

Dacă nu doriți să definiți o funcție înainte de să fie folosită, iar definirea ei după aceea este imposibil, ce-ar fi să o definiți într-un alt modul?

Din punct de vedere tehnic, tot o definiți mai întâi, dar este mai curat.

Ați putea crea o recursivitate de genul următor:

def foo():
    bar()

def bar():
    foo()

Funcțiile Python sunt anonime, la fel cum sunt anonime și valorile, dar pot fi legate de un nume.

În codul de mai sus, foo() nu apelează o funcție cu numele foo, ci apelează o funcție care se întâmplă să fie legată de numele foo în momentul în care se face apelul. Este posibil să se redefinească foo în altă parte, iar bar ar apela apoi noua funcție.

Problema dvs. nu poate fi rezolvată, deoarece este ca și cum ați cere să obțineți o variabilă care nu a fost declarată.

Comentarii

    51

  • pe scurt, dacă aveți if __name__ == ‘__main__’: main() ca ultimă linie în scriptul dumneavoastră, totul va fi în regulă! –  > Por Filipe Pina.
  • @FilipePina Nu am înțeles comentariul tău – de ce nu poți pune ultima linie de cod ca fiind pur și simplu main() ? –  > Por Sanjay Manohar.
  • @SanjayManohar: pentru a evita execuția pe import your_module –  > Por jfs.
  • Aș dori să adaug – uneori este posibil să se ocolească aceste probleme folosind lambdas, deoarece acestea sunt evaluate ulterior. –  > Por Joe.
  • În ceea ce privește „anonim”, vrei să spui de fapt „obiecte de primă clasă”. –  > Por danielm.
Vanya

Ceea ce puteți face este să înfășurați invocarea într-o funcție proprie.

Astfel încât

foo()

def foo():
    print "Hi!"

se va întrerupe, dar

def bar():
    foo()

def foo():
    print "Hi!"

bar()

va funcționa corect.

Regula generală în Python este nu că funcția ar trebui să fie definită mai sus în cod (ca în Pascal), ci că ar trebui să fie definită înainte de utilizarea ei.

Sperăm că vă va fi de ajutor.

Comentarii

    27

  • +1 cel mai direct răspuns, cu conceptul de keystone: Pascal=define mai sus, Python=define mai devreme. –  > Por Bob Stein.
  • Acesta este răspunsul corect, care explică și de ce if __name__=="__main__": soluția funcționează. –  > Por 00prometheus.
  • Dacă înțeleg corect, înseamnă că exemplul spam() și eggs() al lui OP este în regulă așa cum este scris. Este corect? –  > Por krubo.
  • @krubo da, este în regulă așa cum este scris –  > Por lxop.
jldupont

Dacă dați startul scriptului prin următoarele:

if __name__=="__main__":
   main()

atunci probabil că nu trebuie să vă faceți griji cu privire la lucruri precum „declarația de înaintare”. Vedeți, interpretul ar merge să vă încarce toate funcțiile și apoi să pornească funcția main(). Bineînțeles, asigurați-vă că aveți și toate importurile corecte 😉

Dacă stau să mă gândesc bine, nu am auzit niciodată de așa ceva ca „forward declaration” în python… dar, din nou, s-ar putea să mă înșel 😉

Comentarii

    15

  • +1 cel mai practic răspuns: Dacă puneți acest cod la jos din cel mai îndepărtat fișier sursă, atunci sunteți liber să definiți în orice ordine. –  > Por Bob Stein.
  • Un sfat grozav; mă ajută foarte mult; deoarece prefer mult mai mult programarea „de sus în jos” decât cea „de jos în sus”. –  > Por GhostCat.
  • Care este rostul expresiei ” if nume == „principal„: „? Pot să scriu pur și simplu myMain() (sau oricare ar fi numele funcției „main”) la sfârșitul fișierului. –  > Por ThomasRones.
BJ Homer

Dacă apelul către cmp_configs se află în interiorul propriei definiții de funcție, ar trebui să fie în regulă. Vă voi da un exemplu.

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.

În general, plasarea codului în interiorul funcțiilor (cum ar fi main()) va rezolva problema dvs.; trebuie doar să apelați main() la sfârșitul fișierului.

Piotr Czapla

În python nu există un astfel de lucru în python, cum ar fi declarația forward. Trebuie doar să vă asigurați că funcția dvs. este declarată înainte de a fi necesară. rețineți că corpul unei funcții nu este interpretat până când funcția nu este executată.

Luați în considerare următorul exemplu:

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.

Vă puteți gândi că un corp al unei funcții este doar un alt script care va fi interpretat odată ce apelați funcția.

KGardevoir

Îmi cer scuze că am reînviat acest subiect, dar a existat o strategie care nu a fost discutată aici și care ar putea fi aplicabilă.

Utilizând reflecția este posibil să se facă ceva asemănător cu declarația forward. De exemplu, să spunem că aveți o secțiune de cod care arată astfel:

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

În acest fel, am stabilit ce funcție dorim să apelăm înainte ca aceasta să fie definită, fiind vorba de o declarație directă. În python, declarația globals()[function_name]() este același lucru cu foo() if function_name = 'foo' din motivele discutate mai sus, deoarece python trebuie să caute fiecare funcție înainte de a o apela. Dacă ar fi să folosim timeit pentru a vedea cum se compară aceste două instrucțiuni, ele au exact același cost de calcul.

Desigur, exemplul de aici este foarte inutil, dar dacă cineva ar avea o structură complexă care trebuie să execute o funcție, dar care trebuie declarată înainte (sau structural nu are prea mult sens să fie declarată după), se poate stoca pur și simplu un șir de caractere și se poate încerca să se apeleze funcția mai târziu.

unutbu

Nu, nu cred că există vreo modalitate de a declara în avans o funcție în Python.

Imaginați-vă că sunteți interpretul Python. Când ajungeți la linia

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

fie știi ce este cmp_configs, fie nu știi. Pentru a continua, trebuie să știți cmp_configs. Nu contează dacă există recursivitate.

Comentarii

  • Ei bine, acest lucru se întâmplă dacă faceți o singură trecere peste cod. Unele compilatoare (și îmi dau seama că python este interpretat) fac două treceri, astfel încât aceste lucruri să poată fi descoperite. forward-declaration, sau cel puțin un fel de descoperire a domeniului de aplicare, ar fi foarte bun. –  > Por Mark Lakewood.
funroll

Uneori, un algoritm este cel mai ușor de înțeles de sus în jos, pornind de la structura generală și coborând în detalii.

Puteți face acest lucru fără declarații forward:

def main():
  make_omelet()
  eat()

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

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

# ...

main()

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

Ieșire:

Hello, world!

user10488833

Importați fișierul propriu-zis. Presupunând că fișierul se numește test.py:

import test

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

Comentarii

  • dar trebuie să încapsulați tot codul dvs. cu if name pentru a evita ca acesta să se execute de două ori –  > Por arivero.
  • Corect, eu în zilele noastre folosesc foarte mult: ”” if __name__ == ‘__main__’: from test import func func() else: def func(): print(‘Func worked’) ”” Deci este în preferințele utilizatorului final. –  > Por user10488833.
Mike Graham

Nu puteți declara o funcție în avans în Python. Dacă aveți o logică care se execută înainte de a defini funcții, probabil că oricum aveți o problemă. Puneți acțiunea dvs. într-un fișier if __name__ == '__main__' la sfârșitul scriptului (executând o funcție pe care o numiți „main” dacă nu este trivială) și codul dvs. va fi mai modular și îl veți putea folosi ca un modul dacă veți avea vreodată nevoie.

De asemenea, înlocuiți acea înțelegere a listei cu un generator expres (de ex, print "
".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs))
)

De asemenea, nu folosiți cmp, , care este depreciat. Utilizați key și furnizați o funcție de tip „less-than”.

Comentarii

  • Cum furnizez o funcție less-than? –  > Por Nathan Fellman.
  • În loc de cmp_configs, trebuie să definiți o funcție care să primească două argumente și să returneze True dacă primul este mai mic decât al doilea și False în caz contrar. –  > Por Mike Graham.
  • Pentru cei dintre noi care provin dintr-un mediu de tip C, nu este nimic nerezonabil în ceea ce privește executarea logicii înainte de definirea funcțiilor. Gândiți-vă: „compilator cu mai multe pasaje”. Uneori este nevoie de ceva timp pentru a ne adapta la noile limbaje 🙂 –  > Por Luke H.
S.Lott

„doar să îmi reorganizez codul astfel încât să nu mai am această problemă”. Corect. Ușor de făcut. Întotdeauna funcționează.

Puteți furniza întotdeauna funcția înainte de referință.

„Cu toate acestea, există cazuri în care acest lucru este probabil inevitabil, de exemplu atunci când se implementează unele forme de recursivitate”

Nu văd cum ar fi posibil așa ceva. Vă rugăm să furnizați un exemplu de loc în care nu puteți defini funcția înainte de a o utiliza.

Comentarii

  • Eu am o astfel de situație. Încerc să trec tipuri într-un decorator de funcții, iar tipurile sunt definite mai jos în modul. Nu pot muta tipurile incriminate în sus, deoarece acest lucru ar rupe lanțul de moștenire. –  > Por Joe.
  • Am rezolvat problema trecând un lambda în decorator în locul tipului real; dar nu aș ști cum să o rezolv altfel (care nu ar necesita să-mi rearanjez moștenirile).  > Por Joe.
PaulMcG

Acum, așteptați un minut. Când modulul tău ajunge la instrucțiunea print din exemplul tău, înainte de cmp_configs să fi fost definit, ce anume vă așteptați să facă?

Dacă postarea dvs. a unei întrebări folosind print încearcă de fapt să reprezinte ceva de genul acesta:

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

atunci nu este necesar să se definească cmp_configs înainte de a executa această instrucțiune, definiți-o mai târziu în cod și totul va fi bine.

Acum, dacă încercați să faceți referire la cmp_configs ca valoare implicită a unui argument pentru lambda, atunci este o altă poveste:

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

Acum aveți nevoie de un cmp_configs variabilă definită înainte de a ajunge la această linie.

[EDITARE – această parte următoare se pare că nu este corectă, deoarece valoarea implicită a argumentului va fi atribuită atunci când funcția este compilată, iar această valoare va fi utilizată chiar dacă veți schimba valoarea lui cmp_configs mai târziu].

Din fericire, Python, fiind atât de acomodat cu tipurile, așa cum este, nu-i pasă ce pe care îl definiți ca cmp_configs, , astfel încât ați putea prefața cu această afirmație:

cmp_configs = None

Iar compilatorul va fi mulțumit. Asigurați-vă doar că declarați adevăratul cmp_configs înainte de a invoca vreodată fn.

DragonLord

TL;DR: Python face nu are nevoie de declarații forward. Pur și simplu puneți apelurile de funcție în interiorul funcției def definiții, și veți fi în regulă.

def foo(count):
    print("foo "+str(count))
    if(count>0):
        bar(count-1)

def bar(count):
    print("bar "+str(count))
    if(count>0):
        foo(count-1)

foo(3)
print("Finished.")

funcție recursivă definiții, , dă cu succes perfect:

foo 3
bar 2
foo 1
bar 0
Finished.

Cu toate acestea,

bug(13)

def bug(count):
    print("bug never runs "+str(count))

print("Does not print this.")

se întrerupe la nivelul superior invocare a unei funcții care nu a fost încă definită, și dă:

Traceback (most recent call last):
  File "./test1.py", line 1, in <module>
    bug(13)
NameError: name 'bug' is not defined

Python este un limbaj interpretat, ca și Lisp. Nu are verificare a tipurilor, ci doar invocări de funcții în timp de execuție, care reușesc dacă numele funcției a fost legat și eșuează dacă nu este legat.

În mod critic, o funcție def definiție face nu execută niciunul dintre apelurile funcționale din interiorul liniilor sale, ci pur și simplu declară în ce va consta corpul funcției. Din nou, nu face nici măcar o verificare de tip. Așadar, putem face acest lucru:

def uncalled():
    wild_eyed_undefined_function()
    print("I'm not invoked!")

print("Only run this one line.")

și se execută perfect (!), cu ieșire

Only run this one line.

Cheia este diferența dintre definiții și invocări.

Interpretul execută tot ceea ce intră la nivelul superior, ceea ce înseamnă că încearcă să îl invoce. Dacă nu se află în interiorul unei definiții.
Codul dvs. are probleme deoarece ați încercat să invocați o funcție, la nivel superior în acest caz, înainte ca aceasta să fie legată.

Soluția este să plasați invocările de funcții care nu sunt de nivel superior în interiorul unei definiții de funcție, apoi să apelați acea funcție cândva mult mai târziu.

Chestia cu „if __ main __” este un idiom bazat pe acest principiu, dar trebuie să înțelegeți de ce, în loc să îl urmați orbește.

Există cu siguranță subiecte mult mai avansate referitoare la funcțiile lambda și la relegarea dinamică a numelor de funcții, dar acestea sunt nu sunt ceea ce a cerut OP. În plus, acestea pot fi rezolvate folosind aceleași principii: (1) defs define o funcție, nu invocă liniile acestora; (2) aveți probleme atunci când vă invoci un simbol de funcție care nu este legat.

Mikhail

Python nu acceptă declarații forward, dar soluția comună pentru acest lucru este utilizarea următoarei condiții la sfârșitul scriptului/codului dvs:

if __name__ == '__main__': main()

Cu aceasta, va citi mai întâi întregul fișier, apoi va evalua condiția și va apela funcția main(), care va putea apela orice funcție declarată în avans, deoarece a citit deja mai întâi întregul fișier. Această condiție utilizează o variabilă specială __name__ care returnează __main__ valoare ori de câte ori executăm codul Python din fișierul curent (când codul a fost importat ca modul, atunci __name__ returnează numele modulului).

obesechicken13

O modalitate este de a crea o funcție handler. Definiți handlerul la început și puneți handlerul sub toate metodele pe care trebuie să le apelați.

Apoi, atunci când invocați metoda handler pentru a apela funcțiile, acestea vor fi întotdeauna disponibile.

Funcția handler poate primi un argument nameOfMethodToCall. Apoi folosește o serie de instrucțiuni if pentru a apela metoda corectă.

Acest lucru ar rezolva problema dumneavoastră.

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)

Comentarii

  • Asta pare a fi foarte nepionant. Python ar trebui să se ocupe singur de acest tip de lucruri. –  > Por Nathan Fellman.
  • recitiți răspunsul acceptat. Python nu are nevoie ca funcția să fie definită până când nu se apelați o utilizați, nu doar să o folosiți într-o definiție. –  > Por tacaswell.
Satish Reddy Venkannagari

Da, putem verifica acest lucru.

Intrare

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

Ieșire

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.

După cum a menționat BJ Homer în comentariile de mai sus, o regulă generală în Python nu este că funcția ar trebui definită mai sus în cod (ca în Pascal), ci că ar trebui definită înainte de utilizarea sa.

Sper că vă ajută.

Comentarii

  • Nu este print_lyrics() apelată (utilizată) în linia 1 înainte de a fi definită? Am copiat această bucată de cod și am încercat să o execut și îmi dă eroarea NameError: name 'print_lyrics' is not defined la linia 1. Puteți explica acest lucru? –  > Por Bugs Buggy.