Funções
A maioria dos programas executa tarefas grandes o suficiente para serem divididas em várias subtarefas. Por esse motivo, os programadores geralmente dividem seus programas em pequenas partes gerenciáveis conhecidas como funções. Uma função é um grupo de instruções que existem dentro de um programa com o propósito de executar uma tarefa específica. Em vez de escrever um programa com uma longa sequência de instruções, ele pode ser escrito como várias funções pequenas, cada uma desempenhando uma parte específica da tarefa. Essas pequenas funções podem ser executadas na ordem desejada para executar a tarefa geral.
Toda função, por definição, possui um nome, pode receber parâmetros e pode retornar valores. Nem toda função receberá parâmetros, da mesma forma, que nem toda função retornará valores. Tudo dependerá da situação, ainda assim, a estrutura sempre seguirá um padrão e por isso, é facilmente reconhecida. Um programa que foi escrito com cada tarefa em sua própria função é chamado de programa modularizado.
Benefícios da modularização
Funções oferecem uma série de benefícios que as tornam extremamente útil em programas mais complexos:
-
Código simplificado
O código de um programa tende a ser mais simples e mais fácil de entender quando é dividido em funções. Várias funções pequenas são muito mais fáceis de ler do que uma longa sequência de instruções.
-
Reutilização de código
Funções também reduzem a duplicação de código dentro de um programa. Se uma operação específica for executada em vários locais em um programa, uma função pode ser escrita uma vez para executar essa operação e, em seguida, ser executada a qualquer momento em que for necessária.
-
Abstração
Para utilizar uma função, só é preciso saber seu nome, as informações necessárias para que a mesma funcione e os resultados/saídas que ela irá produzir, ou seja, qual o seu objetivo. Não precisamos saber como a função funciona, ou se algum outro código é dependente para usá-la. Por exemplo, não é necessário saber como a função print foi implementada para utilizá-la, só é preciso saber que ela imprime a informação passada como parâmetro.
-
Teste e depuração mais fáceis
Quando cada tarefa, dentro de um programa, está contida em sua própria função, o teste e a depuração se tornam mais simples. Os programadores podem testar cada função em um programa individualmente, para determinar se ela executa corretamente sua operação. Isso facilita o isolamento e a correção de erros.
-
Desenvolvimento mais rápido
Consequência da reutilização de código: funções podem ser escritas para as tarefas comumente necessárias, e essas funções podem ser incorporadas em cada programa que precisa delas, acelerando o desenvolvimento. Por exemplo, suponha que uma equipe de programadores esteja desenvolvendo vários programas. Eles descobrem que cada um dos programas executa várias tarefas comuns, como solicitar um nome de usuário e uma senha, exibir a hora atual e assim por diante. Não faz sentido escrever o código para essas tarefas várias vezes. Em vez disso, as funções podem ser escritas para as tarefas comumente necessárias, e essas funções podem ser incorporadas em cada programa que precisa delas.
-
Trabalho em equipe facilitada
Quando um programa é desenvolvido como um conjunto de funções, então diferentes programadores podem ser designados para o trabalho de escrever diferentes funções.
Funções integradas
O interpretador do Python possui várias funções que estão sempre disponíveis para uso. Essas funções são chamadas de funções integradas (built-in functions). Exemplos de algumas funções integradas: print
, type
, int
, float
, str
, input
, abs
, bin
, isinstance
, filter
, map
, list
, tuple
, len
, pow
, round
, sum
, tuple
, etc.
No site Python Built-in Functions ou Python Built-In Functions você encontra uma lista de funções predefinidas disponíveis pelo interpretador do Python. Veja alguns exemplos de execução/utilização dessas funções:
>>> abs(-1)
1
>>> round(2.4)
2
>>> round(2.5)
2
>>> round(2.6)
3
>>> round(2.51)
3
>>> round (3.5)
4
>>> round(3.7587, 3)
3.759
>>> round(3.7587, 2)
3.76
>>> round(3.7587, 0)
4.0
>>> round(3.7587)
4
>>> isinstance(10, int)
True
>>> isinstance(10.0, int)
False
Abra o Python Shell e teste essas funções.
Funções matemáticas
Por padrão o Python disponibiliza poucos recursos, mas pode-se criar ou utilizar recursos criados por outras pessoas. Para se utilizar recursos externos é necessário importar um módulo (subprogramas externos). Um dos módulos mais usados é o matemático, que oferece a maioria das funções matemáticas comuns.
Um módulo é um arquivo que contém uma coleção de funções relacionadas.
No site Python Mathematical Functions ou Mathematical functions você encontra uma lista de funções disponíveis no módulo matemático.
Antes de usar as funções de um módulo, é preciso importá-lo com uma instrução de importação:
Para acessar uma das funções, é preciso especificar o nome do módulo e o nome da função, separados por um ponto (.
). Veja alguns exemplos:
>>> import math
>>> math.sqrt(2) # raiz quadrada de 2
1.4142135623730951
>>> math.pi # constante matemática pi
3.141592653589793
>>> math.e # constante matemática e
2.718281828459045
>>> math.floor(2.5) # piso
2
>>> math.ceil(2.5) # teto
3
>>> math.log10(100) # log de 100 na base 10
2.0
>>> math.log(100) # log de 100 na base e
4.605170185988092
>>> math.log2(100) # log de 100 na base 2
6.643856189774724
>>> math.log(100, 3) # log de 100 na base 3
4.19180654857877
>>> math.cos(2*math.pi) # cosseno de 2*pi
1.0
>>> math.sin(math.pi/2) # seno de pi/2
1.0
>>> math.radians(180) # converte 180 graus para radianos
3.141592653589793
>>> math.degrees(math.pi) # converte pi radianos para graus
180.0
Abra o Python Shell e teste essas funções.
Do mesmo modo como nas funções matemáticas, as funções do Python podem ser compostas, o que significa que você pode usar uma expressão como parte de outra. Por exemplo, você pode usar qualquer expressão como um argumento para uma função:
Você também pode pegar o resultado de uma função e passá-lo como um argumento para outra:
Exercícios
- Escreva um programa que leia a coordenada de dois pontos e calcule a distância entre eles. Lembrando que a distância entre dois pontos é dada por \(d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}\).
- Escreva um programa que solicite ao usuário os coeficientes de uma equação do segundo grau e, em seguida, seu programa deve calcular as raízes reais, se existirem. Caso as raízes não existam, uma mensagem deve ser exibida.
Gerando números aleatórios
Em alguns casos precisamos gerar números aleatórios ou randômicos para testar funções ou fazer simulações. Um número aleatório pode ser entendido como um número tirado ao acaso, sem qualquer ordem ou sequência predeterminada, como em um sorteio. Para gerar número aleatórios em Python, podemos utilizar o modulo random. O módulo traz várias funções para geração de números aleatórios e mesmo números gerados com distribuições não uniformes. As principais funções do módulo são:
-
random.random(): Gera um número aleatório \(x\) tal que \(x \in [0, 1)\), ou seja, \(0.0 \leq x < 1.0\). Exemplo:
-
random.uniform(a, b) Gera um número aleatório \(x\) tal que \(x \in [a, b]\), ou seja, \(a \leq x \leq b\). Exemplo:
-
random.randrange(a): Gera um número inteiro \(x\) tal que \(x \in [0, a)\), ou seja, \(0 \leq x \leq a - 1\). Podemos definir o valor inicial do sorteio usando o comando random.randrange(a, b), nesse caso, \(x \in [a, b)\). Exemplo:
-
random.randint(a, b): Gera um número inteiro \(x\) tal que \(x \in [a, b]\), ou seja, \(0 \leq x \leq b\). Equivalente a random.randrange(a, b+1). Exemplo:
Definindo novas funções
Por enquanto, só usamos funções que vêm com o Python, mas também é possível criar novas funções. Uma definição de função especifica o nome da nova função e a sequência de instruções que são executadas quando a função é chamada. A figura abaixo apresenta a estrutura de definição de funções em Python:
Fonte: Learn Python By Example
A primeira linha é conhecida como o cabeçalho da função, o resto é chamado de corpo da função. O cabeçalho começa com a palavra-chave def
, precisa terminar em dois pontos e o corpo precisa ser indentado. O nome da função segue as mesmas regras de nome de variáveis. A lista de parâmetros especifica que informação, se houver alguma, você tem que fornecer para poder usar a nova função. A lista de parâmetros pode ser vazia ou conter qualquer número de parâmetros separados por vírgulas. Uma função deve ter ao menos um comando e eles precisam ser indentados. Como veremos, algumas funções possuem um comando return
para retornar algum valor da função.
A lista de expressão de retorno não é avaliada imediatamente. Ela é armazenada como parte da função recém-definida e avaliada somente quando a função é eventualmente aplicada/chamada. Se o usuário não especificar uma expressão de retorno, o Python implicitamente retorna o valor None
do tipo NoneType
(tente fazer print(print("olá"))
e veja o que será impresso).
Vamos definir uma função bem simples que apenas imprime uma mensagem na tela:
Essa primeira função possui o nome mensagem
e os parênteses vazios depois do nome indicam que esta função não usa argumentos. A instrução def
apenas cria uma função, mas não a chama. Na realidade, a definição prepara o interpretador para executar a função quando esta for chamada em outras partes do programa. Para chamar uma função definida no programa, faremos da mesma forma que as funções já definidas na linguagem, ou seja, nome da função seguido dos parâmetros entre parênteses:
Perceba que essa função não possui explicitamente um valor a ser retornado e, como já mencionado, essa função retornará None. Dessa forma, ao ser feito, por exemplo, print(mensagem())
também será impresso None
.
def mensagem():
print("Introdução à Programação")
print(mensagem())
# Introdução à Programação
# None
Uma vez que a função tenha sido definida, é possível usá-la dentro de outra função.
def mensagem():
print("Introdução à Programação")
def mensagemRepetida():
mensagem()
mensagem()
mensagemRepetida()
# Introdução à Programação
# Introdução à Programação
Este programa contém duas definições de função: mensagem
e mensagemRepetida
. As instruções dentro da função não são executadas até que a função seja chamada e a definição de função não gera nenhuma saída.
Observe que é preciso criar uma função antes de executá-la. Em outras palavras, a definição de função tem que ser executada antes que a função seja chamada. O código abaixo, onde a função mensagemRepetida
é definida antes de mensagem
, também funciona. Por quê?
def mensagemRepetida():
mensagem()
mensagem()
def mensagem():
print("Introdução à Programação")
mensagemRepetida()
# Introdução à Programação
# Introdução à Programação
Entretanto, o código abaixo gera erro (NameError
). Por quê?
def mensagemRepetida():
mensagem()
mensagem()
mensagemRepetida()
def mensagem():
print("Introdução à Programação")
# NameError: name 'mensagem' is not defined
Atenção
Lembre-se que quando uma função é chamada, ela e todas as outras funções que ela depende, devem, obrigatoriamente, já terem sido definidas.
Vamos analisar uma função que precisa de informação (parâmetros) para "funcionar". Os parâmetros da função (separados por vírgula) são variáveis locais, portanto, podem ser usados dentro da função. Eles são usados como um meio para comunicação entre as funções, pois recebem (ou retornam) valores de outras funções. Como exemplo, vamos definir uma função soma
que recebe dois números como parâmetros e imprime a soma dos valores.
A função anterior utiliza dois parâmetros e imprime sua soma. Essa função não retorna valores como a função round
ou a int
. Vamos reescrever essa função de forma que o valor da soma seja retornado:
def soma(x, y):
return x + y
print(soma(10, 15)) # 25
print(soma(2, 3)) # 5
print(soma(20, 23)) # 43
A palavra-chave return
é utilizada para declarar a informação a ser retornada pela função. A mesma funciona também para finalizar a execução do bloco de instrução da função, retornando assim, o valor que estiver à sua frente. Então, a instrução return
é utilizada tanto para o retorno de valores pelas funções, como também para finalizar a execução da função. Uma função sem a instrução return
é finalizada no fim do bloco de instruções. Observe que agora retiramos o print
da função soma
. Isso é interessante porque a soma e a impressão da soma de dois números são dois problemas diferentes. Nem sempre vamos somar e imprimir a soma, por isso, vamos deixar a função realizar apenas o cálculo. Se precisarmos imprimir o resultado, podemos utilizar a função print
, como no exemplo.
Uma função pode ter mais de uma instrução return
. Por exemplo:
def positivoOuNegativo():
x = int(input("Digite um número: ))
if x >= 0:
return "Positivo"
else:
return "Negativo"
print(positivoOuNegativo())
O que será impresso pelo programa anterior se for digitado 1? E se for digitado -1? E se for digitado 0?
Devemos tomar cuidado com o tipo de retorno de uma função. A função anterior retorna um string e não deve ser usada em outra parte do código que espera, por exemplo, um número. Por exemplo, os códigos abaixo geram erros:
num = positivoOuNegativo()/3 # TypeError: unsupported operand type(s) for /: 'str' and 'int'
x = int(positivoOuNegativo()) # ValueError: invalid literal for int() with base 10
Como dito, quando não utilizamos o comando return
ou não informamos nenhum valor para o return
a função retorna o valor None
. Veja um exemplo:
def soma(x, y):
z = x + y
def subtracao(x, y):
z = x - y
return # ou return None
resposta1 = soma(2, 3)
resposta2 = subtracao(2, 3)
print(resposta1, resposta2)
# None None
Atenção
Quando a instrução return
é utilizada sem a definição de um valor/expressão a ser retornado, por padrão, o valor que será retornado é None
. Analise o código abaixo, o que será impresso se for digitado 15? E se for digitado 20?
Esse é um exemplo clássico. Perceba que a função menor()
só retorna True
se a idade for menor que 18, caso contrário, será retornado None
, uma vez que não foi definido, explicitamente, o que deve ser retornado caso a idade seja maior ou igual a 18. Lembre-se sempre de tratar todos os casos.
Além disso, os comandos depois de um return
são desconsiderados, uma vez que a instrução finaliza a execução da função.
Vamos agora implementar uma função que recebe um valor inteiro e retorna True
se o valor for par e False
, caso contrário
Essa função pode ser simplificada da seguinte forma:
Por que a simplificação anterior (com a remoção da cláusula else
) funciona?
Podemos simplificar mais ainda:
Nessa última função é comparado se o resto da divisão de x
por 2
é zero. Se for, automaticamente é retornado True
(0 == 0
), caso contrário é retornado False
.
Exercícios
- Faça uma função que receba o raio de uma esfera e retorne a área e o volume da mesma.
- Faça uma função, chamada
maior
, que receba dois valores e retorne o maior valor. - Faça uma função, chamada
maior3
, que receba três valores e, utilizando a funçãomaior
, retorne o maior deles.
Agora vamos fazer um programa que imprima os números primos menores que \(n\) (\(n \in \mathbb{N}\)). Primeiramente, para simplificar, vamos definir uma função que verifica se um dado número natural é primo ou não, chamaremos essa função de ehPrimo
. Vamos também fazer uma função main()
responsável por fazer a leitura do valor de \(n\) e por imprimir os números primos (usando a função ehPrimo
).
def ehPrimo(n):
i = 2
while i*i <= n:
if n % i == 0: # (1)
return False
i += 1
return True
def main():
n = int(input())
if n > 0:
i = 2
while i <= n:
#Imprime apenas os números que são primos entre [1, n]
if ehPrimo(i):
print(i)
i += 1
main()
- Se for encontrado um divisor entre 2 e \(\sqrt{n}\), o número não é primo.
Função main()
Se você tiver alguma experiência com outras linguagens de programação, como C/C++ ou Java, sabe que a função main()
é necessária para executar funções. Como você viu nos exemplos anteriores, isso não é necessariamente necessário para Python. No entanto, incluir uma função main()
em seu programa Python pode ser útil para manter o código bem organizado - todos os componentes mais importantes estão contidos nesta função main()
.
Como a chamada da função main()
fica no final do código, não precisamos nos preocupar com a ordem em que as outras funções são definidas.
Pergunta
O que aconteceria com código anterior, ao ser executado, se a linha 11 fosse comentada?
Exercício
Faça uma função que recebe um valor em segundos e imprime este valor em horas, minutos e segundos. Em seguida, crie uma função main()
que utilize a função de conversão. Por exemplo:
Retorno de múltiplos valores
Os exemplos de funções com retorno que foram analisados até agora retornam apenas um valor. No entanto, em Python, é possível retornar vários valores (separados por vírgula) após a instrução return
, conforme mostrado abaixo:
Veja um exemplo:
def operacoes():
a = int(input())
b = int(input())
return a, b, a + b, a - b, a * b, a / b
def main():
x, y, soma, subtracao, multiplicacao, divisao = operacoes()
print(f"{x} + {y} = {soma}")
print(f"{x} - {y} = {subtracao}")
print(f"{x} * {y} = {multiplicacao}")
print(f"{x} / {y} = {divisao}")
main()
Perceba que a função operacoes
retorna seis valores, logo precisamos atribuir esses seis valores a seis variáveis. Se o número de variáveis que receberão os valores for menor/maior que o número de valores retornado, será gerado um erro: ValueError: too many values to unpack.
Variáveis locais e globais
Quando usamos funções, começamos a trabalhar com variáveis internas ou locais e com variáveis externas ou globais. A diferença entre elas é a visibilidade ou escopo.
Uma variável local a uma função existe apenas dentro dela, sendo normalmente inicializada a cada chamada. Assim, não podemos acessar o valor de uma variável local fora da função que a criou e, por isso, passamos parâmetros e retornamos valores nas funções, de forma a possibilitar a troca de dados no programa.
Uma variável global é definida fora de uma função, pode ser vista por todas as funções do módulo e por todos os módulos que importam o módulo que a definiu.
O código abaixo possui duas variáveis: universidade
(variável global) e, dentro da função, campus
(variável local). A variável universidade
, por ser global, pode ser acessada em qualquer trecho do código (após sua declaração). Já a variável campus
só pode ser acessada dentro da função nomeUF()
.
universidade = "Ufes" # (1)
def nomeUF():
campus = "Ceunes" # (2)
print(f"{universidade}/{campus}")
nomeUF()
# Ufes/Ceunes
- Variável global (declarada fora de qualquer função). Desta forma, após a sua declaração, é possível ter acesso ao seu valor em qualquer trecho do código.
- Variável local (declarada dentro de uma função). Seu escopo é local, não sendo possível ter acesso a tal variável fora da função.
Variáveis locais são removidas da memória quando a chamada de função é encerrada. Portanto, tentar obter o valor da variável local fora da função causa um erro.
Variáveis globais devem ser utilizadas o mínimo possível em seus programas, pois dificultam a leitura e violam o encapsulamento da função. A dificuldade da leitura é em procurar pela definição e conteúdos fora da função em si, que podem mudar entre diferentes chamadas. Além disso, uma variável global pode ser alterada por qualquer função, tornando a tarefa de saber quem altera seu valor realmente mais trabalhosa. O encapsulamento é comprometido porque a função depende de uma variável externa, ou seja, que não é declarada dentro da função nem recebida como parâmetro.
Devido à capacidade da linguagem Python de declarar variáveis à medida que precisarmos, devemos tomar cuidado quando alteramos uma variável global dentro de uma função. Veja um exemplo:
- Uma variável local com o mesmo nome de uma global, "esconde" a variável global.
Na linha 1, criamos uma variável global universidade
. Na linha 4, temos a variável local da função, também
chamada universidade
, recebendo o valor "Ufes/Ceunes"
(uma variável local com o mesmo nome de uma global, "esconde" a variável global). Nas linhas 7 e 9, imprimimos o valor da variável global universidade
e na linha 8, o valor da variável local universidade
.
Atenção
Podemos acessar uma variável global de dentro de uma função. No entanto, se tentarmos modificar a variável global dentro de uma função como no exemplo abaixo será gerado um erro.
x = 1
def funcao()
x += 1
print(x)
funcao() # UnboundLocalError: local variable 'x' referenced before assignment
Isso ocorre porque só podemos acessar a variável global, mas não podemos modificá-la de dentro da função.
Se quisermos modificar uma variável global dentro de uma função, devemos informar que estamos usando uma variável global antes de inicializá-la, na primeira linha de nossa função. Essa definição é feita com a instrução global
. Veja o exemplo modificado:
Agora, na linha 4, estamos trabalhando com a variável global universidade
. Assim, quando fizemos universidade = "Ufes/Ceunes"
, na linha 5, trocamos o valor da variável global universidade
.
Lembre-se de limitar o uso de variáveis globais.
Parâmetros opcionais
Nem sempre será preciso passar todos os parâmetros para uma função, sendo possível utilizar um valor previamente escolhido como padrão (default), mas deixando a possibilidade de alterá-lo, caso necessário. Por exemplo:
No código anterior, x
e y
são parâmetros obrigatórios e imprimeCompleto
é um parâmetro opcional.
Embora seja possível combinar parâmetros opcionais e obrigatórios, eles não podem ser misturados entre si, e os parâmetros opcionais devem sempre ser os últimos. Veja uma definição inválida:
Essa definição é inválida porque o parâmetro opcional imprimeCompleto
é seguido por parâmetros obrigatórios x
e y
.
Atenção
Se uma função possui \(n\) parâmetros obrigatórios, você deve chamá-la com \(n\) argumentos. Veja um exemplo:
Funções puras
Uma função pura é aquela que não provoca efeitos colaterais, ou seja, ela não muda qualquer estado na aplicação. Além disso, uma função pura sempre gera o mesmo resultado com os mesmos argumentos, ou seja, ela é completamente determinística. Em resumo, uma função pura é aquela que:
- Não muda qualquer estado na aplicação, ou seja, não provoca nenhum efeito colateral;
- Não depende de uma variável externa (variável global) e não altera nada exterior a ela;
- Gera sempre os mesmos resultados com os mesmos argumentos.
Veja um exemplo:
Note que a funcaoImpura
precisa da variável A
(declarada fora da função, ou seja, uma variável global) para funcionar. Dessa forma, o valor retornado por ela pode ser diferente para o mesmo argumento. Veja que na linha 16, seria impresso 11, mas na linha 19, seria impresso 13. Por outro lado, a funcaoPura
não precisa de nenhuma variável externa e sempre retorna o mesmo resultado para os mesmos argumentos.
Funções de alta ordem
Um poderoso recurso de Python é permitir a passagem de funções como parâmetro. Tais funções são chamadas as funções de alta ordem (High Order Functions - HOF). Uma função é HOF se aceita funções como argumentos ou retorna uma função como resultado ou ambos. Isso permite combinar várias funções para realizar uma tarefa. Python possui algumas funções integradas que são HOF (por exemplo, map e filter) e também podemos criar nossas próprias funções HOF. Vamos a um exemplo:
def soma(a, b):
return a + b
def subtracao(a, b):
return a - b
def imprime(x, y, operacao):
print(operacao(x, y))
Note que a função imprime
recebe dois valores e uma função. Bom, mas como sabemos que o terceiro parâmetro é uma função e não uma variável comum? Veja que dentro do print
, temos operacao(x, y)
, ou seja, os parênteses com os parâmetros indicam que operacao
deve ser uma função. Perceba também que a função passada como parâmetro para a função imprime
deve aceitar exatamente dois parâmetros obrigatórios, uma vez que dentro de imprime
fazemos operacao(x, y)
. Veja como podemos chamar/utilizar a função imprime
:
imprime(10, 15, soma) # (1)
imprime(3, 4, subtracao) # (2)
imprime(3.141592653589793, 3, round) # (3)
- Equivalente a fazer
print(soma(10, 15))
. Resultado:25
. - Equivalente a fazer
print(subtracao(3, 4))
. Resultado:-1
. - Equivalente a fazer
print(round(3.141592653589793, 4))
. Resultado:3.1416
.
Perceba que a função imprime
aceita qualquer função que precise/use dois parâmetros.
Atenção
Como já dito, a função passada como argumento para imprime
deve aceitar exatamente dois parâmetros. Analise o código abaixo:
Ao executar o código anterior, será gerado o seguinte erro: TypeError: duplica() takes 1 positional argument but 2 were given
. Isso porque o operacao(x, y)
na função imprime
será "substituída" por duplica(x, y)
, mas a função duplica
só deve receber um parâmetro.
Funções anônimas
Imagine que você queira escrever uma função que receba um valor e retorne o dobro desse valor. Como você já sabe definir funções em Python usando a palavra-chave def
, provavelmente escreverá algo assim:
Na verdade existe outra maneira de definir essas pequenas funções em Python usando a palavra-chave lambda
. A função a seguir é totalmente equivalente à definida acima:
Esta função não tem nome e é, portanto, chamada de anônima. Como no Python as funções anônimas são declaradas com a palavra-chave lambda
, elas são frequentemente chamadas de funções lambda
. A sintaxe para definir uma função lambda
é a seguinte
Uma função lambda
pode receber qualquer número de argumentos separados por vírgulas, mas deve consistir em uma única expressão. Esta expressão é avaliada e o resultado é retornado. Observe que você não precisa da instrução return
. Por exemplo, a seguinte função anônima calcula o resto da divisão da soma de dois números por dois:
No caso de você precise colocar uma condição em alguma função lambda
, você terá que usar uma expressão ternária:
As instruções condicionais clássicas não funcionam em uma função lambda
.
Para chamarmos uma função lambda
colocamos a função entre parênteses e passando os argumentos imediatamente:
Como alternativa, também é possível atribuir uma função lambda
a uma variável:
Validação de dados
Em algumas situações, precisamos garantir que a informação passada como argumento para uma função é de determinado tipo. Por exemplo, não faz sentido tentar calcular a raiz quadrada de um string
. Em Python, como já vimos, podemos verificar se o valor armazenado em uma variável é de determinado tipo usando a função integrada type
ou a função isinstance
. Veja alguns exemplos:
>>> type(3) == int
True
>>> type(3) == float
False
>>> type(3.0) == float
True
>>> type(3.0) == int
False
>>> isinstance(3, int)
True
>>> isinstance(3.0, float)
True
Dessa forma, podemos utilizar essas funções integradas para validar se o valor armazenado em uma variável é do tipo esperado/desejado:
a = 10
if type(a) == int: #ou isinstance(a, int)
print(f"{a} é inteiro")
else:
print(f"{a} não é inteiro")
a = 10.0
if isinstance(a, int): #ou type(a) == int
print(f"{a} é inteiro")
else:
print(f"{a} não é inteiro")
Exercício
Crie uma função, chamada ehNumero
, que receba um valor e retorne True
se o valor passado como argumento for um número (inteiro ou real) e False
, caso contrário. Defina uma função main()
que exemplifica a utilização da função ehNumero
.
def ehNumero(x):
# if type(x) == int or type(x) == float:
# return True
# return False
return True if type(x) == int or type(x) == float else False
def funcao():
return 1, 2, 3
def main():
print(ehNumero(0))
print(ehNumero("0"))
print(ehNumero(True))
print(ehNumero(3.1415))
x = funcao()
print(x)
print(type(x))
print(ehNumero(funcao()))
main()
# True
# False
# False
# True
# (1, 2, 3)
# <class 'tuple' >
# False
Além disso, quando um programa precisa ler um valor, digamos do tipo int
, e caso o usuário digite um valor que não pode ser convertido para int
, uma exceção é lançada (o que "quebra" com a execução do programa):
Para resolvermos esse problema, usamos tratamento de exceções para validar a entrada de um determinado tipo de dado. Por exemplo, se quisermos/precisarmos evitar que o programa "quebre" ao ser digitado um string
ou float
em um input()
que está esperando um inteiro, podemos fazer o seguinte:
try: #Tenta fazer a conversão do valor digitado para int
x = int(input("Digite um inteiro"))
except: #Se não for possível fazer a conversão, uma exceção é lançada
print("Você deve digitar um valor inteiro")
O código abaixo ilustra um exemplo de uma função responsável pela leitura de um valor inteiro enquanto não for digitado um valor que pode ser convertido para int
.
Exercícios
-
Implemente a função
leFloat
e teste o programa.- O que acontece se você digitar um caractere? E se for digitado um valor inteiro?
- Qual a diferença na implementação entre as funções
leInteiro
eleFloat
? - Seria possível implementar uma função genérica para ser usada tanto para ler inteiros quanto floats?
-
Implemente a função
leValor
. Essa função deve ser uma função de alta ordem que recebe o tipo esperado (int
oufloat
oustr
) e, se necessário, a mensagem doinput()
. Teste sua implementação com o código abaixo.
Documentação
Uma definição de função geralmente inclui documentação que descreve a função, chamada docstring. Docstrings são convencionalmente inseridos entre três aspas duplas. Embora opcionais são fortemente recomendados. Veja um exemplo abaixo. A primeira linha descreve o objetivo da função em uma linha. As seguintes linhas podem descrever argumentos e esclarecer o comportamento da função:
def converteTempo(h, m, s):
"""
Converte horas, minutos e segundos em segundos.
Lembrando que uma hora possui 3600 segundos e um minuto equivale a 60 segundos.
Parâmetros:
- h: horas
- m: minutos
- s: segundos
Retorno:
Retorna a quantidade de segundos contidos em h horas + m minutos + s segundos.
Exemplos de uso:
print(converteTempo(1, 15, 20))
segundos = converteTempo(2, 0, 32)
"""
return h * 3600 + m * 60 + s
print(converteTempo(1, 10, 15)) # converte 1h10min15seg em segundos
# 4215
Para imprimir o docstring
de uma função, use a função integrada help() e passe o nome da função.
help(converteTempo)
# Help on function converteTempo in module __main__:
#
# converteTempo(h, m, s)
# Converte horas, minutos e segundos em segundos.
# Lembrando que uma hora possui 3600 segundos e um minuto equivale a 60 segundos.
#
# Parâmetros:
# - h: horas
# - m: minutos
# - s: segundos
#
# Retorno:
# Retorna a quantidade de segundos contidos em h horas + m minutos + s segundos.
#
# Exemplos de uso:
# print(converteTempo(1, 15, 20))
# segundos = converteTempo(2, 0, 32)
Dica
No VSCode se você passar o mouse sobre o nome da função, será exibido o docstring
da função. A extensão autoDocstring - - Python Docstring Generator te ajuda na criação dos docstring
das funções.
Fluxo de execução
Para garantir que uma função seja definida antes do seu primeiro uso, é preciso saber a ordem na qual as instruções serão executadas. Isso é chamado de fluxo de execução. A execução sempre começa na primeira instrução do programa. As instruções são executadas uma após a outra, de cima para baixo.
As definições de função não alteram o fluxo da execução do programa, mas lembre-se de que as instruções dentro da função não são executadas até a função ser chamada.
Uma chamada de função é como um desvio no fluxo de execução. Em vez de ir à próxima instrução, o fluxo salta para o corpo da função, executa as instruções lá, e então volta para continuar de onde parou. Parece bastante simples, até você lembrar que uma função pode chamar outra. Enquanto estiver no meio de uma função, o programa pode ter que executar as instruções em outra função. Então, enquanto estiver executando a nova função, o programa pode ter que executar mais uma função!
Felizmente, o Python é bom em não perder o fio da meada, então cada vez que uma função é concluída, o programa continua de onde parou na função que o chamou. Quando chega no fim do programa, ele é encerrado. Vamos analisar o fluxo de execução do código abaixo:
def soma():
print("Dentro da função soma()")
x = int(input("Digite um valor: "))
y = int(input("Digite outro valor: "))
print(f"{x} + {y} = {x + y}")
print("Antes de chamar a função soma()")
soma()
print("Depois de chamar a função soma()")
Fluxo de execução:
Material Complementar
Complemente sua leitura e seu conhecimento: