2013-04-03 28 views
6

Mam niektóre kodu Pythona, który zasadniczo wygląda następująco:Python: Funkcja zawsze zwraca None

my_start_list = ... 

def process (my_list): 
    #do some stuff 

    if len(my_list) > 1: 
     process(my_list) 
    else: 
     print(my_list) 
     return my_list 

print(process(my_start_list)) 

Najdziwniejsze jest: drukowania (my_list) wypisuje poprawnej zawartości. Jednak druga instrukcja print wyświetlająca wartość zwracaną przez funkcję zawsze drukuje "None". Nawet jeśli zastąpię zwykłą instrukcję return z return ("abc"), to nadal jest Brak.

Ponieważ treść zmiennej wydaje się być poprawna w jednym wierszu przed instrukcją return, nie wiem, gdzie rozpocząć debugowanie. Czy są jakieś typowe problemy, które mogą powodować takie problemy?

+10

użyć 'return process (my_list)' podczas wywoływania go rekurencyjnie. –

+4

@NPE Zakładam, że '#do some stuff' zawiera modyfikację listy w jakiś sposób; inaczej masz rację; to powinno się powtarzać w nieskończoność. –

+1

pokrewne: http://stackoverflow.com/questions/11097822/weird-function-return-value –

Odpowiedz

8

Oto co się dzieje:

  1. zadzwonić process(my_start_list).
  2. W funkcji blok if jest wykonywany, jeśli len(my_list) > 1, i nie ma tam instrukcji return.
    Tutaj, ponieważ else nie został wykonany i ponieważ jest to jedyne miejsce, w którym masz klauzulę powrotu, zwracasz wartość domyślną, czyli None.
  3. Jeśli masz 0 lub 1 element na liście, zwrócisz tę listę.

Więc powrót do listy zwrócony przez process(my_list)
czyli:

def process(my_list): 
    #do some stuff 
    ... 
    if len(my_list) > 1: 
     return process(my_list) 
    else: 
     print(my_list) 
     return my_list 
10

Zwracasz listę tylko wtedy, gdy masz 1 lub 0 elementów (podstawowy przypadek). Potrzebujesz instrukcji return również w pierwszym bloku, gdzie wykonujesz rekursywne wywołanie, lub gdy przechodzisz do podstawowej wersji, zwracasz listę długości-1 do następnego poziomu, a następnie zwracasz None do końca. . Więc co chcesz wygląda tak, zamiast:

def process(my_list): 
    # Do some stuff. 
    if len(my_list) > 1: 
     return process(my_list) #If you don't return this result, you return None 
    else: 
     print(my_list) 
     return my_list 

Teraz każdy przypadek (nie tylko w przypadku podstawa) ma wartość powrotną, tak że wartość zwracana będzie propagować całą drogę z powrotem do pierwszego połączenia.

+0

Prawda, ale pojedynczy punkt wyjścia (powrotu) jest zawsze lepszy. –

+0

Jego funkcja jest rekurencyjna; on będzie kończył się co najmniej dwoma zwrotami, bez względu na to, co robi (jeden dla przypadku podstawowego i jeden dla wywołania rekursywnego). –

+4

@marksweb: ** Dlaczego ** czy pojedynczy powrót byłby lepszy? Zostało to omówione na śmierć, ale to już nie jest prawdą. Zobacz [Czy funkcja ma tylko jedną instrukcję zwrotu?] (Http://stackoverflow.com/q/36707) –

3

Rekurencyjnie wywołujesz process, ale nigdy nie ignoruj ​​jego wartości zwracanej. Dodaj return oświadczenie przejściu na wartości zwracanej ::

def process (my_list): 
    #do some stuff 

    if len(my_list) > 1: 
     return process(my_list) 
    else: 
     print(my_list) 
     return my_list 

Teraz, kiedy len(my_list) > 1 jest True, rzeczywiście przekazać wartości zwracanej rekurencyjnego wywołania.

2

Jak inni zwrócili uwagę, tracisz return oświadczenie.

Osobiście kolei, że ogon rekursji do iteracji:

def process(my_list): 
    while True: 
     # do some stuff 
     if len(my_list) <= 1: 
      return my_list 

myślę, że to sprawia, że ​​zamiar trochę jaśniejsze, a także pozwala uniknąć niektórych pitfalls associated with tail recursion.

Powiązane problemy