zwróć, zwróć Brak i w ogóle nie zwróć?

386

Rozważ trzy funkcje:

def my_func1():
  print "Hello World"
  return None

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Wszystkie wydają się zwracać Brak. Czy są jakieś różnice między zachowaniem zwracanej wartości tych funkcji? Czy są jakieś powody, by preferować jedno od drugiego?

Clay Wardell
źródło
40
Pamiętaj, że istnieje różnica stylistyczna. return Nonesugeruje mi, że funkcja czasami ma Nonewartość zwrotną, ale w miejscu return Nonetakiej wartości zwrotnej nie ma. Brak returnzapisu sugeruje, że nigdy nie ma interesującej wartości zwracanej, takiej jak „procedura” w przeciwieństwie do „funkcji”. returnimplikuje istnienie wcześnie na podstawie „procedury” zgodnie z poprzednim punktem.

Odpowiedzi:

500

Od rzeczywistego zachowania nie ma różnicy. Wszyscy wracają Nonei to wszystko. Jest jednak czas i miejsce na wszystkie z nich. Poniższe instrukcje przedstawiają w zasadzie, w jaki sposób należy stosować różne metody (a przynajmniej tak, jak mnie nauczono, jak należy ich używać), ale nie są to bezwzględne zasady, więc możesz je mieszać, jeśli uważasz, że jest to konieczne.

Za pomocą return None

To mówi, że funkcja rzeczywiście ma zwrócić wartość do późniejszego użycia, w tym przypadku zwraca None. Tę wartość Nonemożna następnie wykorzystać w innym miejscu. return Nonenigdy nie jest używane, jeśli nie ma innych możliwych wartości zwracanych z funkcji.

W poniższym przykładzie zwracamy person, motherjeśli persondany jest człowiekiem. Jeśli to nie jest człowiek, wracamy, Noneponieważ personnie ma mother(przypuśćmy, że to nie zwierzę ani coś takiego).

def get_mother(person):
    if is_human(person):
        return person.mother
    else:
        return None

Za pomocą return

Jest to używane z tego samego powodu, co breakw pętlach. Zwracana wartość nie ma znaczenia i chcesz tylko wyjść z całej funkcji. Jest to niezwykle przydatne w niektórych miejscach, nawet jeśli nie jest to tak często potrzebne.

Mamy 15 prisonersi wiemy, że jeden z nich ma nóż. Pętlimy po prisonerkolei, aby sprawdzić, czy mają nóż. Jeśli uderzymy osobę nożem, możemy po prostu wyjść z funkcji, ponieważ wiemy, że jest tylko jeden nóż i nie ma powodu, aby sprawdzić resztę prisoners. Jeśli nie znajdziemy prisonernoża, alarmujemy. Można to zrobić na wiele różnych sposobów, a użycie returnprawdopodobnie nie jest nawet najlepszym sposobem, ale jest to tylko przykład pokazujący, jak używać returndo wychodzenia z funkcji.

def find_prisoner_with_knife(prisoners):
    for prisoner in prisoners:
        if "knife" in prisoner.items:
            prisoner.move_to_inquisition()
            return # no need to check rest of the prisoners nor raise an alert
    raise_alert()

Uwaga: Nigdy nie należy tego robić var = find_prisoner_with_knife(), ponieważ wartość zwracana nie jest przeznaczona do przechwycenia.

W ogóle nie returnużywam

To również zwróci None, ale ta wartość nie jest przeznaczona do użycia ani przechwycenia. Oznacza to po prostu, że funkcja zakończyła się powodzeniem. Zasadniczo jest taki sam, jak returnw voidfunkcjach w językach takich jak C ++ lub Java.

W poniższym przykładzie ustawiamy imię matki osoby, a następnie funkcja kończy działanie po pomyślnym zakończeniu.

def set_mother(person, mother):
    if is_human(person):
        person.mother = mother

Uwaga: Nigdy nie należy tego robić var = set_mother(my_person, my_mother), ponieważ wartość zwracana nie jest przeznaczona do przechwycenia.

Josh Downes
źródło
5
var = get_mother()jest OK, jeśli przechodzisz vardo innych funkcji, które akceptują None- „nigdy” jest trochę ekstremalne
joelb
Jak należy przyjąć wartość zwracaną, jeśli var = get_mother()nie można jej użyć? IMHO to w porządku. Musisz tylko sprawdzić, czy nie ma Nonewartości if varprzed użyciem.
winklerrr,
Pomyślałem, że ważne jest, aby wspomnieć, że nie jest to tylko dobra konwencja, jest wymagana przez PEP8. Wysyłam odpowiedź, aby wyrazić uznanie i zacytować PEP8 w tej sprawie.
Fabiano
29

Tak, wszystkie są takie same.

Możemy przejrzeć zinterpretowany kod maszynowy, aby potwierdzić, że wszyscy robią dokładnie to samo.

import dis

def f1():
  print "Hello World"
  return None

def f2():
  print "Hello World"
  return

def f3():
  print "Hello World"

dis.dis(f1)
    4   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    5   5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f2)
    9   0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE

    10  5 LOAD_CONST    0 (None)
        8 RETURN_VALUE

dis.dis(f3)
    14  0 LOAD_CONST    1 ('Hello World')
        3 PRINT_ITEM
        4 PRINT_NEWLINE            
        5 LOAD_CONST    0 (None)
        8 RETURN_VALUE      
David Marks
źródło
4
Ostrożnie tutaj ... dis.diszwraca None: -X
mgilson
Jedynym sposobem na uzyskanie wyniku dis jako łańcucha jest przekierowanie standardowego wyjścia do jakiegoś bufora IO (np. StringIO). Nawet wtedy bezpośrednie porównywanie w ten sposób może nie działać, ponieważ moim zdaniem dis.diszgłasza również niektóre numery linii ...
mgilson
Nie ma znaczenia, czy i tak używają dokładnie tych samych rejestrów, więc zmieniłem kod, więc po prostu przyglądamy się kodowi maszynowemu, zamiast robić jakieś głupkowate porównanie ciągów. Dzięki za bardzo duże heads-upy.
David Marx,
19

Każdy z nich zwraca ten sam singleton None- nie ma różnicy funkcjonalnej.

Myślę, że rozsądnie idiomatyczne jest pominięcie returninstrukcji, chyba że trzeba jej wcześnie wyjść z funkcji (w takim przypadku nagi returnwystępuje częściej) lub zwrócić coś innego niż None. Ma to również sens i wydaje się idiomatyczne do pisania, return Nonegdy jest w funkcji, która ma inną ścieżkę, która zwraca coś innego niż None. Piśmie return Nonez wyraźnie jest wizualną wskazówkę dla czytelnika, że istnieje inny oddział, który zwraca coś bardziej interesujący (i to wywołanie kodu będzie prawdopodobnie trzeba obsługiwać oba typy wartości zwracanej).

Często w Pythonie, funkcje, które zwracają Nonesłużą jak voidfunkcje w C - Ich celem jest generalnie działają na argumentach wejściowych na miejscu (chyba, że używasz danych globalnych ( dreszcze )). Zwracanie Nonezwykle czyni bardziej wyraźnym, że argumenty zostały zmutowane. To sprawia, że ​​nieco bardziej jasne jest, dlaczego warto pominąć to returnstwierdzenie z punktu widzenia „konwencji językowych”.

To powiedziawszy, jeśli pracujesz w bazie kodu, która ma już ustalone konwencje dotyczące tych rzeczy, zdecydowanie podążę za tym, aby baza kodu pozostała jednolita ...

mgilson
źródło
1
Lub bardziej ogólnie, za każdym razem, gdy funkcja kończy się bez trafienia w returninstrukcję, zwraca None.
Nie jest idiomatyczne pominięcie instrukcji return, jeśli funkcja ma zwrócić wartość. Tylko funkcje działające wyłącznie na skutek działań niepożądanych powinny pomijać instrukcję return.
cząsteczka
@molecule - Tak, myślę, że chyba nie zrobiłem najlepszej roboty w moim wyjaśnieniu tutaj. Próbowałem go zaktualizować, nie czyniąc z niego pełnego klonu (o wiele lepszej) odpowiedzi powyżej. Nadal nie jestem bardzo zadowolony z tego postu ... Część mnie chce go usunąć (ponieważ odpowiedź powyżej jest o wiele lepsza), a część mnie chce go zachować - 9 osób do tej pory uważało, że jest dobry dla z tego czy innego powodu. Może trafiłem na coś, za czym ktoś tęsknił?
mgilson,
1
@ZAB - Nie sądzę, że to możliwe. W Pythonie mogę małpować łatanie wszystkiego z czymkolwiek (jest to zgodne z projektem i jest to świetna funkcja, jeśli jest używana oszczędnie). Konkretnie, mogę małpować łatanie funkcji, która ma zwrot z tą, która nie ma. Wszystkie kontrole „bez wyrażenia” odbywają się w czasie analizy, ale z powodu łatania małp nie mogło się to zdarzyć do czasu uruchomienia. IOW, jest zupełnie inaczej. Osobiście uważam, że obecne zachowanie jest dość rozsądne (i zgodne z innymi językami wysokiego poziomu), ale nie jestem tym, którego musisz przekonać :-).
mgilson
1
@ZAB - oba Rubyi Javascriptprzyjmują takie samo podejście jak python. Jestem pewien, że tam przykłady języków wysokiego poziomu, które mówią „void” w wyrażeniu, ale nie mogę myśleć o każdy w tej chwili (może jeśli wziąć pod uwagę Javajęzyk wysokiego poziomu) ... W jaki sposób małpa-łatanie funkcja (która jest przydatna między innymi do wyśmiewania się w testach) działa w hipotetycznym świecie, w którym python otrzymał voidsłowo kluczowe, które ją sprawiło, że funkcja nic nie zwróciła? (Ponadto, jak powiedziałem wcześniej, nie musisz mnie przekonywać, że to zły projekt. Nie mogę nic zrobić, nawet jeśli masz rację)
mgilson 13.01.18
4

Jak inni odpowiedzieli, wynik jest dokładnie taki sam, Nonejest zwracany we wszystkich przypadkach.

Różnica jest stylistyczna, ale należy pamiętać, że PEP8 wymaga spójnego użycia:

Zachowaj spójność w deklaracjach zwrotnych. Albo wszystkie instrukcje return w funkcji powinny zwracać wyrażenie, albo żadna z nich nie powinna. Jeśli jakakolwiek instrukcja return zwraca wyrażenie, wszelkie instrukcje return, w których nie jest zwracana żadna wartość, powinny wyraźnie podawać to jako return None, a wyraźna instrukcja return powinna znajdować się na końcu funkcji (jeśli jest osiągalna).

Tak:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

Nie:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

https://www.python.org/dev/peps/pep-0008/#programming-recommendations


Zasadniczo, jeśli kiedykolwiek zwrócisz Nonewartość w funkcji, oznacza to, że zwracana wartość ma znaczenie i jest przeznaczona do przechwycenia przez osoby dzwoniące. Więc kiedy wracasz None, musi być również jawne, aby przekazać Nonew tym przypadku ma znaczenie, jest to jedna z możliwych wartości zwracanych.

Jeśli w ogóle nie potrzebujesz zwrotu, funkcja działa w zasadzie jako procedura zamiast funkcji, więc po prostu nie dołączaj returninstrukcji.

Jeśli piszesz funkcję podobną do procedury i istnieje możliwość powrotu wcześniej (tj. Już skończyłeś i nie musisz wykonywać pozostałej części funkcji), możesz użyć pustych znaków returns do zasygnalizowania czytnikowi jest to tylko wczesne zakończenie wykonywania, a Nonewartość zwrócona niejawnie nie ma żadnego znaczenia i nie jest przeznaczona do przechwycenia (funkcja podobna do procedury i tak zawsze zwraca wartość None).

Fabiano
źródło
3

Pod względem funkcjonalności są one takie same, różnica między nimi polega na czytelności kodu i stylu (co należy wziąć pod uwagę)

Yehuda Schwartz
źródło