повернення, повернення Ні, і взагалі повернення?


386

Розглянемо три функції:

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

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Вони, здається, не повертаються Ніхто. Чи є різниці між тим, як поводяться повернені значення цих функцій? Чи є якісь причини віддавати перевагу одному проти іншого?


40
Зауважте, що існує стилістична різниця. return Noneдля мене означає, що функція іноді має Noneнеповернене значення, але в місці розташування return Noneтакого поверненого значення немає. Писання "ні" returnвзагалі не означає, що ми ніколи не цікаво повертаємо значення, ніби "процедура" на відміну від "функції". returnМає на увазі існування на ранніх етапах "процедури" згідно з попереднім пунктом.

Відповіді:


500

Від фактичної поведінки різниці немає. Вони всі повертаються, Noneі це все. Однак є час і місце для всього цього. Наступні інструкції - це в основному способи використання різних методів (або, принаймні, як мене вчили, що їх слід використовувати), але вони не є абсолютними правилами, тому ви можете їх змішати, якщо вам здається необхідним.

Використання return None

Це говорить про те, що функція дійсно призначена для повернення значення для подальшого використання, і в цьому випадку вона повертається None. Це значення Noneможе бути використане в іншому місці. return Noneніколи не використовується, якщо немає інших можливих повернених значень функції.

У наступному прикладі ми повертаємось person, motherякщо personдана людина. Якщо це не людина, ми повертаємося, Noneоскільки personне має mother(припустимо, це не тварина чи щось таке).

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

Використання return

Це використовується з тієї ж причини, що і breakв петлях. Повернене значення не має значення, і ви хочете лише вийти з усієї функції. Це вкрай корисно в деяких місцях, навіть якщо він вам не потрібен так часто.

У нас 15, prisonersі ми знаємо, що в одного з них є ніж. Ми пров'язуємо кожен prisonerпо одному, щоб перевірити, чи є у них ніж. Якщо ми вдаримо людину ножем, ми можемо просто вийти з функції, оскільки ми знаємо, що є лише один ніж, і немає причини перевірити решту prisoners. Якщо ми не знайдемо prisonerножа, ми піднімаємо попередження. Це можна зробити різними способами, і використання return, мабуть, навіть не найкращого способу, але це лише приклад, який показує, як використовувати returnдля виходу з функції.

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

Примітка. Ніколи цього не слід робити var = find_prisoner_with_knife(), оскільки повернене значення не має бути спійманим.

Використовуючи не returnзовсім

Це також повернеться None, але це значення не призначене для використання або спіймання. Це просто означає, що функція закінчилася успішно. Це в основному те саме, що returnу voidфункціях на таких мовах, як C ++ або Java.

У наступному прикладі ми встановлюємо ім'я матері людини, а потім функція закінчується після успішного завершення.

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

Примітка. Ніколи цього не слід робити var = set_mother(my_person, my_mother), оскільки повернене значення не має бути спійманим.


5
var = get_mother()гаразд, якщо ви переходите varдо інших функцій, які приймають None- "ніколи" не є крайнім
joelb

Як слід прийняти повернене значення, якщо var = get_mother()його не можна використовувати? ІМХО це абсолютно добре робити. Вам просто потрібно переконатися, що перед тим, як користуватися, перевірити наявність не Noneзначення if var.
winklerrr

Я вважав, що важливо згадати, що це не просто хороша конвенція, це вимагає PEP8. Я надсилаю відповідь на комплімент вашим і цитую PEP8 з цього приводу.
Фабіано

29

Так, вони всі однакові.

Ми можемо переглянути інтерпретований машинний код, щоб підтвердити, що всі вони роблять точно те саме.

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      

4
обережно тут ... dis.disповертається None: -X
mgilson

Єдиний спосіб отримати вихід dis у вигляді рядка - це перенаправити stdout до якогось IO буфера (наприклад, StringIO). Навіть тоді порівняння безпосередньо подібне може не працювати, як я вважаю, dis.disтакож повідомляє про деякі рядки ...
mgilson

Неважливо, чи все-таки вони використовують такі самі регістри, чи я змінив код, щоб ми просто подивилися машинним кодом зараз, замість того, щоб робити якесь порівняння рядкових рядків. Дякую за дуже великі голови вгору.
Девід Маркс

19

Кожен з них повертає один і той же сингтон None- функціональної різниці немає.

Я вважаю, що цілком ідіоматично залишати returnзаяву, якщо ви не потребуєте її рано вийти з функції (у такому випадку гола returnчастіше) або повернути щось інше, ніж None. Це також має сенс і, здається, ідіоматично писати, return Noneколи це у функції, яка має інший шлях, який повертає щось інше, ніж None. Виписка return Noneчітко - це візуальна підказка для читача про те, що є ще одна гілка, яка повертає щось цікавіше (і що для викликового коду, ймовірно, потрібно буде обробляти обидва типи повернених значень).

Найчастіше в Python функції, що повертаються None, використовуються як voidфункції в C - Їх мета, як правило, працювати над вхідними аргументами на місці (якщо ви не використовуєте глобальні дані ( здригання )). Повернення Noneзазвичай робить більш очевидним, що аргументи було вимкнено. Це робить трохи більш зрозумілим, чому є сенс відмовлятися від returnтвердження з точки зору "мовних конвенцій".

Це означає, що якщо ви працюєте в кодовій базі, яка вже має попередньо встановлені умови щодо цих речей, я, безумовно, слідкую за прикладом, щоб допомогти кодовій базі залишатися рівномірною ...


1
Або в більш загальному випадку , кожен раз , коли функція закінчується без удару returnо, він повертається None.

Не ідіоматично опускати оператор return, якщо очікується, що функція поверне значення. Тільки функції, які функціонують виключно через побічні ефекти, повинні опускати операцію повернення.
молекула

@molecule - Так, я думаю, що, можливо, я не зробив найкращої роботи в своєму поясненні тут. Я намагався оновити його, не роблячи його повним клоном (набагато кращого) відповіді вище. Я все ще не надто задоволений цією публікацією ... Частина мене хоче видалити її (оскільки відповідь вище набагато краща), а частина мене хоче зберегти її - 9 людей поки що вважали, що це добре тієї чи іншої причини. Можливо, я потрапив на щось, що хтось пропустив?
mgilson

1
@ZAB - Я не думаю, що це можливо. У python я можу мавпочки зафіксувати що завгодно чим завгодно (це за дизайном, і це чудова функція, якщо використовувати їх економно). Зокрема, я можу зафіксувати функцію, яка має повернення з тією, яка не відповідає. Усі перевірки "не на вираз" трапляються під час розбору, але через патч-мавпи це не могло відбутися до часу виконання. IOW, це зовсім інше. Особисто я вважаю, що поточна поведінка цілком розумна (і відповідає іншим мовам високого рівня), але я не той, кого потрібно переконувати :-).
mgilson

1
@ZAB - і те, Rubyі інше - Javascriptтакий самий підхід, як і python. Я впевнений, що є приклади мов високого рівня, які говорять про "недійсні" у виразі, але я не можу придумати жодного на даний момент (можливо, якщо ви вважаєте мову Javaвисокого рівня) ... функція (яка корисна для глузування з тестів серед іншого) працює в гіпотетичному світі, де python отримав voidключове слово, яке зробило його таким чином, що функція нічого не повертає? (Крім того, як я вже говорив раніше, вам не потрібно переконувати мене, що це погана конструкція. Я нічого не можу зробити, навіть якщо ви маєте рацію)
mgilson

4

Як відповіли інші, результат точно такий же, Noneповертається у всіх випадках.

Різниця стилістична, але зауважте, що PEP8 вимагає, щоб використання було послідовним:

Будьте послідовними у заявах про повернення. Або всі оператори return у функції повинні повертати вираз, або жодне з них не повинно. Якщо будь-який оператор return повертає вираз, будь-які оператори повернення, де значення не повертається, повинні прямо вказати це як return None, а явний оператор return повинен бути присутній в кінці функції (якщо він доступний).

Так:

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)

Ні:

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


В основному, якщо ви коли-небудь повертаєте нецінність Noneу функції, це означає, що значення, що повертається, має значення і повинно сприйматися абонентами. Отже, коли ви повертаєтесь None, це також повинно бути явним, щоб передати Noneв цьому випадку має сенс, це одне з можливих значень повернення.

Якщо взагалі не потрібно повернення, функція в основному працює як процедура, а не функція, тому просто не включайте returnоператор.

Якщо ви пишете функцію, що нагадує процедуру, і є можливість повернутися раніше (тобто ви вже зробили в цей момент і не потрібно виконувати решту функції), ви можете використовувати порожній returns для передачі сигналу для читача це лише дострокове завершення виконання, і Noneзначення, яке повертається неявно, не має жодного значення і не призначене для спіймання (подібна до процедури функція завжди Noneвсе одно повертається ).


3

З точки зору функціональності вони однакові, різниця між ними полягає в читанні коду та стилі (що важливо враховувати)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.