Czy można zadeklarować dalej funkcję w Pythonie?

189

Czy można zadeklarować dalej funkcję w Pythonie? Chcę posortować listę za pomocą własnej cmpfunkcji, zanim zostanie zadeklarowana.

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

Zorganizowałem swój kod, aby wstawić definicję cmp_configsmetody po wywołaniu. Błąd tego błędu:

NameError: name 'cmp_configs' is not defined

Czy jest jakiś sposób na „zadeklarowanie” cmp_configsmetody przed jej użyciem? Czy dzięki temu mój kod będzie wyglądał na czystszy?

Zakładam, że niektórzy ludzie będą mieli ochotę powiedzieć mi, że powinienem po prostu zreorganizować swój kod, aby nie miałem tego problemu. Są jednak przypadki, w których jest to prawdopodobnie nieuniknione, na przykład przy wdrażaniu niektórych form rekurencji. Jeśli nie podoba ci się ten przykład, załóż, że mam przypadek, w którym naprawdę konieczne jest przekazanie dalej zadeklarowanej funkcji.

Rozważ ten przypadek, w którym w Pythonie konieczne byłoby przekazywanie funkcji do przodu:

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

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

Gdzie end_conditioni end_resultzostały wcześniej zdefiniowane.

Czy jedynym rozwiązaniem jest reorganizacja kodu i zawsze umieszczanie definicji przed wywołaniami?

Nathan Fellman
źródło

Odpowiedzi:

76

Jeśli nie chcesz definiować funkcji przed jej użyciem, a późniejsze jej zdefiniowanie jest niemożliwe, co z definiowaniem jej w innym module?

Technicznie nadal definiujesz go jako pierwszy, ale jest czysty.

Możesz utworzyć rekurencję w następujący sposób:

def foo():
    bar()

def bar():
    foo()

Funkcje Pythona są anonimowe, podobnie jak wartości są anonimowe, ale można je przypisać do nazwy.

W powyższym kodzie foo()nie wywołuje funkcji o nazwie foo, wywołuje funkcję powiązaną z nazwą foow momencie wywołania. Można zdefiniować foogdzie indziej, a barnastępnie wywołać nową funkcję.

Twój problem nie może zostać rozwiązany, ponieważ przypomina prośbę o uzyskanie zmiennej, która nie została zadeklarowana.

RichN
źródło
47
w skrócie, jeśli masz jeśli __name__ == '__main__': main () jako ostatni wiersz skryptu wszystko będzie dobrze!
Filipe Pina
3
@FilipePina Nie zrozumiałem twojego komentarza - dlaczego nie możesz tak po prostu umieścić ostatniego wiersza kodu main()?
Sanjay Manohar
11
@SanjayManohar: aby uniknąć wykonywania goimport your_module
jfs,
2
Chciałbym dodać - czasami można obejść te problemy za pomocą lambd, ponieważ są one oceniane później.
Joe
2
Pisząc „anonimowy”, naprawdę masz na myśli „obiekty pierwszej klasy”.
danielm
119

Co możesz zrobić, to owinąć wywołanie we własną funkcję.

Po to aby

foo()

def foo():
    print "Hi!"

pęknie, ale

def bar():
    foo()

def foo():
    print "Hi!"

bar()

będzie działać poprawnie.

Ogólna zasada niePython polega na tym, że funkcja powinna być zdefiniowana wyżej w kodzie (jak w ), ale że należy ją zdefiniować przed użyciem.Pascal

Mam nadzieję, że to pomaga.

Vanya
źródło
20
+1 najbardziej bezpośrednia odpowiedź, z koncepcją klucza: Pascal = zdefiniuj wyżej, Python = zdefiniuj wcześniej.
Bob Stein
1
To poprawna odpowiedź, wyjaśnia również, dlaczego if __name__=="__main__":rozwiązanie działa.
00prometheus,
2
Jeśli dobrze rozumiem, oznacza to, że przykład spamu () i jaj () OP jest w porządku, jak napisano. Czy to jest poprawne?
krubo
1
@krubo tak, jest w porządku, jak napisano
lxop
92

Jeśli uruchomisz skrypt w następujący sposób:

if __name__=="__main__":
   main()

wtedy prawdopodobnie nie musisz się martwić takimi sprawami jak „deklaracja przekazania”. Widzisz, interpreter ładuje wszystkie twoje funkcje, a następnie uruchamia twoją funkcję main (). Oczywiście upewnij się, że wszystkie importy również są poprawne ;-)

Pomyśl o tym, nigdy nie słyszałem czegoś takiego jak „deklaracja przekazania” w pythonie ... ale z drugiej strony, mogę się mylić ;-)

Jldupont
źródło
14
+1 najbardziej praktyczna odpowiedź: jeśli umieścisz ten kod na dole najbardziej zewnętrznego pliku źródłowego, możesz dowolnie definiować w dowolnej kolejności.
Bob Stein
2
Świetna wskazówka; naprawdę mi pomaga; ponieważ znacznie bardziej wolę programowanie „z góry na dół” niż z dołu do góry.
GhostCat,
10

Jeśli wywołanie cmp_configs mieści się w jego własnej definicji funkcji, wszystko powinno być w porządku. Dam przykład.

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.

Zasadniczo umieszczenie kodu w funkcjach (takich jak main ()) rozwiąże problem; wystarczy wywołać main () na końcu pliku.

BJ Homer
źródło
10

Przepraszam za ożywienie tego wątku, ale nie była tutaj omawiana strategia, która może mieć zastosowanie.

Za pomocą refleksji można zrobić coś podobnego do przekazania deklaracji. Załóżmy na przykład, że masz sekcję kodu, która wygląda następująco:

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

W ten sposób ustaliliśmy, jaką funkcję chcemy wywołać, zanim zostanie ona faktycznie zdefiniowana, w efekcie deklaracja przekazania. W python oświadczenie globals()[function_name]()jest taka sama, jak foo()gdyby function_name = 'foo'z powodów omówionych powyżej, ponieważ Python musi odnośnika każdą funkcję przed nazywając ją. Gdyby użyć timeitmodułu, aby zobaczyć porównanie tych dwóch instrukcji, mają one dokładnie taki sam koszt obliczeniowy.

Oczywiście przykład tutaj jest bardzo bezużyteczny, ale jeśli ktoś miałby mieć złożoną strukturę, która wymagałaby wykonania funkcji, ale musiałaby zostać zadeklarowana wcześniej (lub strukturalnie nie ma sensu mieć jej później), można po prostu zapisać ciąg znaków i spróbuj wywołać tę funkcję później.

KGardevoir
źródło
9

W pythonie nie ma czegoś takiego jak deklaracja przesyłania dalej. Musisz tylko upewnić się, że twoja funkcja jest zadeklarowana, zanim będzie potrzebna. Zauważ, że treść funkcji nie jest interpretowana, dopóki funkcja nie zostanie wykonana.

Rozważ następujący przykład:

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.

Możesz pomyśleć, że ciało funkcji jest tylko kolejnym skryptem, który zostanie zinterpretowany po wywołaniu funkcji.

Piotr Czapla
źródło
7

Nie, nie sądzę, aby w Pythonie można było zadeklarować dalej funkcję.

Wyobraź sobie, że jesteś interpretatorem języka Python. Kiedy dojdziesz do linii

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

albo wiesz, co to jest cmp_configs, albo nie. Aby kontynuować, musisz znać cmp_configs. Nie ma znaczenia, czy nastąpi rekurencja.

unutbu
źródło
9
cóż, dzieje się tak, jeśli wykonujesz tylko jedno przejście kodu. Niektóre kompilatory (i zdaję sobie sprawę, że Python jest zinterpretowany) wykonują dwa przejścia, aby można było to rozgryźć. Deklaracja do przodu, a przynajmniej jakieś odkrycie o zasięgu, byłoby naprawdę dobre.
Mark Lakewood,
7

Czasami algorytm jest najłatwiejszy do zrozumienia z góry na dół, zaczynając od ogólnej struktury i przechodząc do szczegółów.

Możesz to zrobić bez deklaracji przesyłania dalej:

def main():
  make_omelet()
  eat()

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

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

# ...

main()
funroll
źródło
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()

Wynik:

Hello, world!
jmurphy61
źródło
3

Nie można zadeklarować do przodu funkcji w Pythonie. Jeśli masz logikę wykonywaną przed zdefiniowaniem funkcji, prawdopodobnie i tak masz problem. Umieść swoją akcję if __name__ == '__main__'na końcu skryptu (wykonując funkcję, którą nazwiesz „main”, jeśli nie jest trywialna), a twój kod będzie bardziej modułowy i będziesz mógł użyć go jako modułu, jeśli kiedykolwiek będziesz potrzebować do.

Zastąp też tę listę zrozumieniem generatora ekspresowego (tj. print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Nie używaj również cmp, co jest przestarzałe. Użyj keyi podaj mniej niż funkcję.

Mike Graham
źródło
Jak zapewnić funkcję mniej niż?
Nathan Fellman,
Zamiast cmp_configs zdefiniowałbyś funkcję, która pobiera dwa argumenty i zwraca True, jeśli pierwszy jest mniejszy niż drugi, a False w przeciwnym razie.
Mike Graham
Dla tych z nas, którzy pochodzą z tła typu C, nie ma nic nierozsądnego w logice wykonującej się przed zdefiniowaniem funkcji. Pomyśl: „kompilator wieloprzebiegowy”. Czasami dostosowanie się do nowych języków zajmuje trochę czasu
Luke H
3

Zaimportuj sam plik. Zakładając, że plik nazywa się test.py:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
użytkownik10488833
źródło
1

„po prostu zreorganizuj mój kod, aby nie miałem tego problemu”. Poprawny. Proste do zrobienia. Zawsze działa.

Zawsze możesz podać funkcję przed jej odwołaniem.

„Są jednak przypadki, w których jest to prawdopodobnie nieuniknione, na przykład podczas wdrażania niektórych form rekurencji”

Nie widzę, jak to jest nawet możliwe. Podaj przykład miejsca, w którym nie można zdefiniować funkcji przed jej użyciem.

S.Lott
źródło
Mam taką sytuację. Próbuję przekazać typy w dekoratorze funkcji, a typy są zdefiniowane w dalszej części modułu. Nie mogę przesunąć przestępczych typów w górę, ponieważ spowodowałoby to przerwanie łańcucha spadkowego.
Joe
Naprawiłem to, przekazując lambda mojemu dekoratorowi zamiast rzeczywistego typu; ale nie wiedziałbym, jak to naprawić inaczej (nie wymagałoby to ode mnie zmiany dziedzictwa)
Joe
0

Teraz poczekaj chwilę. Kiedy Twój moduł osiągnie instrukcję print w twoim przykładzie, zanim cmp_configszostał zdefiniowany, czego dokładnie oczekujesz?

Jeśli Twoje pytanie za pomocą print naprawdę próbuje przedstawić coś takiego:

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

wtedy nie ma wymogu zdefiniowania cmp_configsprzed wykonaniem tej instrukcji, wystarczy zdefiniować ją później w kodzie i wszystko będzie dobrze.

Teraz, jeśli próbujesz odwołać się cmp_configsdo domyślnej wartości argumentu do lambda, to jest inna historia:

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

Teraz potrzebujesz cmp_configszdefiniowanej zmiennej, zanim dojdziesz do tej linii.

[EDYCJA - ta kolejna część okazuje się niepoprawna, ponieważ domyślna wartość argumentu zostanie przypisana podczas kompilacji funkcji, a ta wartość zostanie wykorzystana, nawet jeśli później zmienisz wartość cmp_configs.]

Na szczęście Python, który jest tak dostosowany do typów, nie przejmuje się tym , co definiujesz jako cmp_configs, więc możesz po prostu przedłożyć to zdanie:

cmp_configs = None

A kompilator będzie szczęśliwy. Pamiętaj tylko, aby zadeklarować prawdziwe, cmp_configszanim zaczniesz wzywać fn.

PaulMcG
źródło
-1

Jednym ze sposobów jest utworzenie funkcji modułu obsługi. Zdefiniuj moduł obsługi na wczesnym etapie i umieść moduł obsługi pod wszystkimi metodami, które musisz wywołać.

Następnie po wywołaniu metody modułu obsługi w celu wywołania funkcji będą one zawsze dostępne.

Przewodnik może wziąć argument nameOfMethodToCall. Następnie używa zestawu instrukcji if do wywołania właściwej metody.

To rozwiązałoby problem.

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)
obesechicken13
źródło
to wydaje się bardzo mało mityczne. Python powinien sam obsługiwać tego rodzaju rzeczy.
Nathan Fellman
ponownie przeczytaj zaakceptowaną odpowiedź. Python nie wymaga zdefiniowania tej funkcji, dopóki jej nie wywołasz , a nie tylko jej użycia w definicji.
tacaswell
-3

Tak, możemy to sprawdzić.

Wejście

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

Wynik

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.

Jak wspomniano powyżej BJ Homer, ogólną zasadą w Pythonie nie jest to, że funkcja powinna być zdefiniowana wyżej w kodzie (jak w Pascalu), ale że należy ją zdefiniować przed użyciem.

Mam nadzieję, że to pomaga.

Satish Reddy Venkannagari
źródło
2
Nie jest print_lyrics()wywoływany (używany) w wierszu 1, zanim zostanie zdefiniowany? Skopiowałem ten fragment kodu i próbowałem go uruchomić, co powoduje błąd NameError: name 'print_lyrics' is not definedw wierszu 1. Czy możesz to wyjaśnić?
Bugs Buggy,