Consideremos a listagem seguinte:
01.
>>>
def
toto(n):
02.
...
print
n
+
1
03.
...
04.
>>>
def
titi(n):
05.
...
return
n
+
1
06.
...
07.
>>> toto(
4
)
08.
5
09.
>>> titi(
4
)
10.
5
11.
>>>
print
toto(
4
)
12.
?
13.
>>>
print
titi(
4
)
14.
?
15.
>>> toto(
4
)
+
1
16.
?
17.
>>> titi(
4
)
+
1
18.
?
O que vai parecer no lugar dos pontos de interrogação? Esta era uma das perguntas de um dos testes. O que estava em jogo essencialmente era saber em que medida se compreendia a diferença entre existir ou não a instrução de
return e a diferença para um
print. Pedia-se também a
justificação e isso era muito importante para a aceitar a resposta.
Vejamos dois exemplos de resposta. São apenas indicativos dos problemas evidenciados por um número muito grande de alunos.
Resposta 1
Na linha 12 (primeiro ponto de interrogação) o resultado será 5 e na linha 14 (segundo ponto de interrogação) também será 5, a diferença dos dois é que na função
toto, os valores serão retornados por um
print e na função
titi serão retornadas por um
return. Na linha 16 (terceiro ponto de interrogação) irá dar um ewrro, pois não se pode somar +1 a uma função e na linha 18 (quarto ponto de interrogação) como a função retorna um valor pela instrução
return o resultado será 6.
Resposta 2
Linha 12:
aparecerá
>>> 5
>>> 5
Porque há duas instruções de
print, uma dentro da função e outra de fora dela.
Linha 14:
Aparecerá
>>> 5
Porque o resultado apenas é imprimido uma vez.
Linha 16:
Aparecerá
>>> 6
Porque mais uma vez a impressão é feita apenas uma vez.
Linha 18:
Aparecerá
>> None
Porque o resultado não foi mandado imprimir.
Vamos então ver os problemas. Comecemos por um facto: trata-se de uma sessão no interpretador. Assim sendo, todas as
expressões que colocarmos estão sujeitas a um processo que se desdobra em três fases: leitura da expressão, cálculo do seu valor e impressão do resultado. Trata-se do ciclo
READ-EVAL-PRINT já referido nas aulas. Repito: aplica-se a todas as expressões. E o que é uma
expressão? Pode ser um objecto (com valor), um nome associado a um objecto, ou uma função/método aplicada/o aos seus argumentos. Vejamos um exemplo.
01.
>>> a
=
5
02.
>>>
7
03.
7
04.
>>> a
05.
5
06.
>>> a
+
5
07.
10
08.
>>>
import
math
09.
>>> math.sin(
3
)
10.
0.1411200080598672
11.
>>> math.sin(a)
12.
-
0.9589242746631385
13.
>>> math.sin(math.sin(a))
14.
-
0.8185741444617193
15.
>>>
Quando damos um objecto (7) o respectivo valor é ecoado; quando introduzimos o nome
a é calculado o valor do objecto associado e este é ecoado. Quando introduzimos uma
expressão mais complexa ela é avaliada e o valor do objecto resultado é ecoado. Podemos ter expressões mais bizarras como a que envolve calcular o seno do seno de um número.
O leitor atento perguntará: mas porque é que no primeiro caso da listagem (
a = 5), nada é ecoado? A razão é simples: porque não é uma expressão mas antes uma
instrução. É por essa mesma razão que quando definimos no interpretador uma função, nada é ecoado. Estamos apenas a definir usando a instrução composta
def. Mas quando
usamos uma definição, ela tem a natureza de uma expressão e por isso
o valor que devolve vai ser ecoado!
01.
>>>
def
dobro(n):
02.
...
return
2
*
n
03.
...
04.
>>> dobro(
3
)
05.
6
06.
>>> dobro(dobro(
2
))
07.
8
08.
>>> dobro(
4
)
+
1
<
-
-
Uso
09.
9
10.
>>>
Mas como sabemos qual é o valor que a chamada (o uso) de uma função devolve? Simples! É o valor da expressão associada ao primeiro
return encontrado durante a execução da função. Mas, e se a função não tiver
nenhum return ??? Já sabemos a resposta: devolve na mesma um objecto chamado
None! Mas devemos ter em atenção que
None denota a
ausência de valor!!
1.
>>>
None
2.
>>>
print
None
3.
None
4.
>>>
Daí que
None não seja ecoado pelo interpretador mas seja impresso por
print.
Percebido isto, já estamos em condições de responder com correcção à pergunta. Ou talvez não... Ora vamos lá a ver outra situação.
1.
>>> a
=
5
2.
>>> a
3.
5
4.
>>>
print
a
5.
5
6.
>>>
Mas última situação retratada, não deveria aparecer duas vezes o valor 5? Uma pelo
print e a outra porque o interpretador depois de ler, avaliar imprime o resultado? Não, não devia, porque
print a não é uma expressão! Logo só há a impressão devido ao
print. Então e se a instrução de
print aparecer no interior de uma definição. O que é que acontece? Olhemos para a primeira situação da pergunta:
1.
>>>
def
toto(n):
2.
...
print
n
+
1
3.
...
4.
>>> toto(
4
)
5.
5
6.
>>>
Expliquemos de novo: definir a função
toto não faz ecoar nada. Mas quando usamos a função, chamando com o argumento 4 (
toto(4)) porque aparece 5? Apenas 5! Afinal
toto(4) é uma expressão. Sim, é verdade, mas na ausência de um
return o objecto devolvido ao interpretador é
None que, como dissemos denota a
ausência de valor. Então o 5 que aparece resulta da instrução de
print dentro da definição. UIff! Agora é que está tudo certo.
Regressemos então a sessão da pergunta.
01.
>>>
def
toto(n):
02.
...
print
n
+
1
03.
...
04.
>>>
def
titi(n):
05.
...
return
n
+
1
06.
...
07.
>>> toto(
4
)
08.
5
09.
>>> titi(
4
)
10.
5
11.
>>>
print
toto(
4
)
12.
?
13.
>>>
print
titi(
4
)
14.
?
15.
>>> toto(
4
)
+
1
16.
?
17.
>>> titi(
4
)
+
1
18.
?
As duas definições não ecoam nada. As duas chamadas seguintes fazem aparecer 5. Mas a primeira ocorrência de 5 é devida ao
print , enquanto que a segunda resulta do interpretador imprimir o objecto devolvido pelo
return da definição. Agora o primeiro ponto de interrogação. Vai aparecer:
1.
>>>
print
toto(
4
)
2.
5
3.
None
O 5 resulta da instrução de
print, enquanto que o
None é ecoado pelo interpretador e resulta do facto de não havendo
return esse é o objecto devolvido e que
print tem que imprimir.
Terceiro ponto de interrogação:
Aqui é fácil: a função devolve 5 (devido ao
return), que é ecoado pelo interpretador. Já sabemos que o
print nem era preciso.
Terceiro ponto de interrogação.
1.
>>> toto(
4
)
+
1
2.
5
3.
Traceback (most recent call last):
4.
File
"<string>"
, line
1
,
in
<fragment>
5.
TypeError: unsupported operand type(s)
for
+
:
'NoneType'
and
'int'
6.
>>>
7.
<
/
fragment><
/
string>
O 5 vem da instrução de
print. O erro deriva do facto de que, na ausência de
return, o valor devolvido é
None, e este objecto não pode ser somado com um inteiro. Logo dá erro.
Quarto ponto de interrogação.
Aqui tudo é mais “normal”: a função devolve 5, este valor é somado a 1, e este é o valor da expressão que o interpretador ecoa.
That’s it!