return, return None e nenhum retorno?

386

Considere três funções:

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

def my_func2():
  print "Hello World"
  return

def my_func3():
  print "Hello World"

Todos eles parecem retornar Nenhum. Existem diferenças entre como o valor retornado dessas funções se comporta? Existem razões para preferir um versus o outro?

Clay Wardell
fonte
40
Observe que há uma diferença estilística. return Noneimplica para mim que a função às vezes tem um Nonevalor de não retorno, mas no local de return None, não existe esse valor de retorno. Escrever não significa returnpara mim que nunca há um valor de retorno interessante, como um "procedimento" em oposição a uma "função". returnimplica a existência antecipada de um "procedimento" conforme o ponto anterior.

Respostas:

500

No comportamento real, não há diferença. Todos eles retornam Nonee é isso. No entanto, há um tempo e um lugar para todos eles. As instruções a seguir são basicamente como os diferentes métodos devem ser usados ​​(ou pelo menos como eu fui ensinado que eles devem ser usados), mas eles não são regras absolutas, portanto você pode misturá-los se achar necessário.

Usando return None

Isso indica que a função realmente deve retornar um valor para uso posterior e, nesse caso, ela retorna None. Esse valor Nonepode ser usado em outro lugar. return Nonenunca será usado se não houver outros valores de retorno possíveis da função.

No exemplo a seguir, retornamos person's motherse o persondado é humano. Se não é humano, retornamos, Nonepois personnão possui um mother(suponha que não seja um animal ou algo assim).

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

Usando return

Isso é usado pelo mesmo motivo que breakem loops. O valor de retorno não importa e você deseja apenas sair de toda a função. É extremamente útil em alguns lugares, mesmo que você não precise com tanta frequência.

Temos 15 prisonerse sabemos que um deles tem uma faca. Percorremos cada prisonerum por um para verificar se eles têm uma faca. Se batermos na pessoa com uma faca, podemos simplesmente sair da função, porque sabemos que há apenas uma faca e não há razão para o resto da verificação prisoners. Se não encontrarmos prisonercom uma faca, acionamos um alerta. Isso pode ser feito de muitas maneiras diferentes e o uso returnprovavelmente não é o melhor, mas é apenas um exemplo para mostrar como usar returnpara sair de uma função.

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

Nota: Você nunca deve fazer isso var = find_prisoner_with_knife(), pois o valor de retorno não deve ser capturado.

Não usar returnem tudo

Isso também retornará None, mas esse valor não deve ser usado ou capturado. Significa simplesmente que a função terminou com sucesso. É basicamente o mesmo que returnem voidfunções em linguagens como C ++ ou Java.

No exemplo a seguir, definimos o nome da mãe da pessoa e a função é encerrada após a conclusão com êxito.

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

Nota: Você nunca deve fazer isso var = set_mother(my_person, my_mother), pois o valor de retorno não deve ser capturado.

Josh Downes
fonte
5
var = get_mother()está OK se você estiver passando varpara outras funções que aceitam None- "never" é um pouco extremo
joelb 16/11/18
Como se deve aceitar o valor de retorno se var = get_mother()não pode ser usado? IMHO, é totalmente bom fazer isso. Você só precisa verificar se não há um Nonevalor if varantes de usá-lo.
winklerrr
Eu pensei que era importante mencionar que esta não é apenas uma boa convenção, é exigida pelo PEP8. Eu posto uma resposta para elogiar a sua e cito o PEP8.
Fabiano
29

Sim, eles são todos iguais.

Podemos revisar o código de máquina interpretado para confirmar se todos estão fazendo exatamente a mesma coisa.

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 Marx
fonte
4
cuidado aqui ... dis.disretorna None: -X
mgilson 8/13
A única maneira de obter a saída de dis como uma string é redirecionar stdout para algum tipo de buffer de E / S (por exemplo, StringIO). Mesmo assim, comparando diretamente como esse pode não funcionar como eu acredito que dis.distambém relata alguns números de linha ...
mgilson
É irrelevante se eles estão usando exatamente os mesmos registros de qualquer maneira, então mudei meu código para ver o código da máquina agora, em vez de fazer algum tipo de comparação pateta. Obrigado pelo grande aviso.
David Marx
19

Cada um deles retorna o mesmo singleton None- Não há diferença funcional.

Eu acho que é razoavelmente idiomático deixar de fora a returndeclaração, a menos que você precise interromper a função mais cedo (nesse caso, um nu returné mais comum) ou retornar algo diferente None. Também faz sentido e parece idiomático escrever return Nonequando está em uma função que possui outro caminho que retorna algo diferente de None. Escrever return Noneexplicitamente é uma dica visual para o leitor de que há outro ramo que retorna algo mais interessante (e que o código de chamada provavelmente precisará lidar com os dois tipos de valores de retorno).

Freqüentemente, em Python, funções que retornam Nonesão usadas como voidfunções em C - seu objetivo geralmente é operar nos argumentos de entrada existentes (a menos que você esteja usando dados globais ( estremecimentos )). O retorno Nonegeralmente torna mais explícito que os argumentos foram alterados. Isso deixa um pouco mais claro o porquê de fazer sentido deixar de lado a returndeclaração do ponto de vista de "convenções linguísticas".

Dito isto, se você estiver trabalhando em uma base de código que já possui convenções pré-definidas sobre essas coisas, eu definitivamente seguiria o exemplo para ajudar a base de código a permanecer uniforme ...

mgilson
fonte
11
Ou, geralmente, sempre que uma função termina sem pressionar uma returninstrução, ela retorna None.
Não é idiomático omitir a declaração de retorno se se espera que a função retorne um valor. Somente funções que operam exclusivamente através de efeitos colaterais devem omitir a declaração de retorno.
molécula é a seguinte
Molecule @ - Sim, acho que talvez não tenha feito o melhor trabalho na minha explicação aqui. Tentei atualizá-lo sem torná-lo um clone completo da (muito melhor) resposta acima. Ainda não estou muito feliz com este post ... Parte de mim deseja excluí-lo (já que a resposta acima é muito melhor) e parte de mim deseja mantê-lo - 9 pessoas até agora pensaram que era bom para uma razão ou outra. Talvez eu tenha batido em algo que alguém perdeu?
mgilson
11
@ ZAB - Eu não acho que isso é possível. Em python, eu posso fazer um patch de macaco com qualquer coisa (isso é por design e é um ótimo recurso se usado com moderação). Especificamente, eu posso consertar um macaco com uma função que tem um retorno com uma que não tem. Todas as verificações "não uma expressão" acontecem no momento da análise, mas devido à correção de macacos, isso não poderia acontecer até o tempo de execução. IOW, é completamente diferente. Pessoalmente, acho que o comportamento atual é bastante razoável (e consistente com outras linguagens de alto nível), mas não sou eu que você precisa convencer :-).
mgilson
11
@ ZAB - ambos Rubye Javascriptseguem a mesma abordagem do python. Tenho certeza de que não são exemplos de linguagens de alto nível que dizem "vazio" na expressão, mas eu não consigo pensar em nenhum no momento (talvez se você considerar Javauma linguagem de alto nível) ... Como seria macaco-patching uma função (que é útil para zombar de testes, entre outras coisas) funciona em um mundo hipotético em que python recebeu uma voidpalavra - chave que a criou para que a função não retornasse nada? (Além disso, como eu disse antes, você não precisa me convencer de que este é um projeto ruim eu não posso fazer nada, mesmo se você está certo.)
mgilson
4

Como outros já responderam, o resultado é exatamente o mesmo, Noneé retornado em todos os casos.

A diferença é estilística, mas observe que o PEP8 exige que o uso seja consistente:

Seja consistente nas declarações de retorno. Todas as instruções de retorno em uma função devem retornar uma expressão, ou nenhuma delas deve. Se qualquer declaração de retorno retornar uma expressão, qualquer declaração de retorno em que nenhum valor seja retornado deve declarar explicitamente isso como retorno Nenhum e uma declaração de retorno explícita deve estar presente no final da função (se possível).

Sim:

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)

Não:

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


Basicamente, se você retornar um Nonevalor sem valor em uma função, isso significa que o valor de retorno tem significado e deve ser capturado pelos chamadores. Portanto, quando você retorna None, também deve ser explícito; transmitir None, nesse caso, tem significado, é um dos possíveis valores de retorno.

Se você não precisar retornar, a função funciona basicamente como um procedimento, e não como uma função, portanto, não inclua a returninstrução

Se você estiver escrevendo uma função semelhante a um procedimento e houver uma oportunidade de retornar mais cedo (ou seja, você já terminou nesse ponto e não precisa executar o restante da função), use returnes vazio para sinalizar para o leitor é apenas um término inicial da execução e o Nonevalor retornado implicitamente não tem nenhum significado e não deve ser capturado (a função semelhante a procedimento sempre retorna de Nonequalquer maneira).

Fabiano
fonte
3

Em termos de funcionalidade, são todos iguais, a diferença entre eles está na legibilidade e no estilo do código (o que é importante considerar)

Yehuda Schwartz
fonte