вернуть, вернуть нет, и нет возврата вообще?

386

Рассмотрим три функции:

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

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Все они, кажется, возвращают None. Есть ли различия между поведением возвращаемых значений этих функций? Есть ли причины отдавать предпочтение одному другому?

Клэй Уорделл
источник
40
Обратите внимание, что есть стилистическая разница. return Noneдля меня это означает, что функция иногда имеет Noneневозвратное значение, но в месте return None, где нет такого возвращаемого значения. Запись no 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
Мгилсон
Единственный способ получить вывод dis в виде строки - перенаправить stdout в какой-нибудь буфер ввода-вывода (например, StringIO). Даже тогда, прямое сравнение может не сработать, так как я считаю, что dis.disтакже сообщает некоторые номера строк ...
mgilson
Не имеет значения, используют ли они в любом случае одни и те же регистры, поэтому я изменил свой код, поэтому теперь мы просто смотрим на машинный код, вместо того, чтобы делать какое-то глупое сравнение строк. Спасибо за очень большие головы.
Дэвид Маркс
19

Каждый из них возвращает один и тот же синглтон None- функциональной разницы нет.

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

Часто в Python функции, которые возвращают None, используются как voidфункции в C - их цель обычно состоит в том, чтобы работать с входными аргументами на месте (если вы не используете глобальные данные ( дрожание )). Возвращение Noneобычно делает более явным, что аргументы были видоизменены. Это делает немного более понятным, почему имеет смысл исключать это returnутверждение с точки зрения «языковых соглашений».

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

mgilson
источник
1
Или, в более общем смысле, каждый раз, когда функция завершается без нажатия на returnоператор, она возвращается None.
Нет смысла опускать оператор return, если ожидается, что функция вернет значение. Только функции, которые работают исключительно через побочные эффекты, должны опускать оператор return.
молекула
@molecule - Да, я думаю, что, возможно, я не сделал лучшую работу в моем объяснении здесь. Я пытался обновить его, не делая его полным клоном (гораздо лучшего) ответа выше. Я все еще не очень доволен этим сообщением ... Часть меня хочет удалить его (так как ответ выше намного лучше), а часть меня хочет сохранить его - 9 человек до сих пор думали, что это хорошо для та или иная причина. Может быть, я наткнулся на то, что кто-то пропустил?
Мгилсон
1
@ ЗАБ - я не думаю, что это возможно. В Python я могу сделать все, что угодно, с помощью чего угодно (это сделано специально, и это отличная возможность, если использовать ее с осторожностью). В частности, я могу обезопасить функцию, которая возвращает результат, с функцией, которая этого не делает. Все проверки «не выражений» выполняются во время разбора, но из-за исправлений обезьяны это не могло произойти до времени выполнения. Я совершенно другой. Лично я считаю, что текущее поведение вполне разумно (и согласуется с другими языками высокого уровня), но я не тот, кого нужно убеждать :-).
Мгилсон
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

С точки зрения функциональности все они одинаковы, разница между ними заключается в удобочитаемости и стиле кода (что важно учитывать)

Иегуда Шварц
источник