05 Aula - Funções
Aula 05: Funções¶
Esta aula é baseada no capítulo 8 do livro "Introdução à programação com Python. Algoritmos e lógica de programação para iniciantes" de Nilo Ney Coutinho Menezes.
Esta é talvez a aula mais importante do curso.
Começamos o curso usando o interpretador (terminal do python na verdade) como uma calculadora. Nessa calculadora incrementada, aprendemos a trabalhar com números (inteiros e de ponto flutuante) e também com strings.
Logo no começo introduzimos um primeiro mecanismo de abstração: variáveis. Com as variáveis, pudemos dar nomes a valores. Aí bastava mudar o valor e executar o programa novamente.
Mas só variáveis não permitem que a gente faça muita coisa. Então aprendemos a usar expressões condicionais. Com isso e variáveis podíamos escolher que parte do programa seria executado.
Mas ainda assim, estávamos bem limitados ao que podíamos fazer. Então introduzimos algumas estruturas de dados mais complexas como
- Listas
- Dicionários
- Tuplas
- Conjuntos
Com isso começamos a implementar programas mais sofisticados, desenvolvendo algoritmos mais complexos (como por exemplo o programa para reordenar listas) e até mesmo joguinhos.
Mas ainda assim temos um problema: como reaproveitar programas ou parte dos programas? Ou executamos o programa novamente ou fazemos um copy and paste.
Aqui que entra o conceito de funções!
O que são funções em Python?¶
O termo surge da semelhança com as funções em matemática como por exemplo a função seno ou cosseno.
$$ y = cos(x) $$Dado um valor representado por x, a função cos
returna um outro valor que corresponde ao cosseno do valor x.
Em Python, a função cosseno é implementada no módulo math
que deve ser carregado:
import math
e agora podemos usar acessar a função que calcula o cosseno como
python-repl
In [1]: import math
In [2]: print(math.cos(0.5))
0.8775825618903728
Muitas vezes é inconveniente ficar digitando math.cos(0.5)
então podemos fazer
python-repl
In [5]: from math import cos
In [6]: print(cos(0.5))
0.8775825618903728
import math
math.cos(0.5)
math.sin(0.5)
math.tan(0.5)
math.sin(0.5) / math.cos(0.5)
math.sin(0.5)**2 + math.cos(0.5)**2
from math import cos
cos(0.5)
A biblioteca padrão do Python implementa um monte de coisa útil para o programador. Dê uma olhada na página https://docs.python.org/pt-br/3/library/index.html.
O módulo math
tem um monte de coisa interessante https://docs.python.org/pt-br/3/library/math.html
Mas agora, o que queremos fazer é aprender a criar as nossas próprias funções.
Vamos começar com algo bem simples (e inútil!). Somar dois números:
def soma(a, b):
print(a+b)
A instrução def
é usada para definir uma nova função. No exemplo acima, é criada a função soma
que tem 2 argumentos a
e b
e o corpo da função, que nesse caso é dado por uma expressão apenas print(a+b)
.
Repare na indentação. Assim como no if
, while
e for
, o corpo da função deve ser indentado.
soma(1,2)
no exemplo acima, chamamos a função soma
com os argumentos 1
e 2
. O argumento da função a
assume o valor 1
e o argumento b
assume o valor 2
.
soma(3,4)
Vamos fazer uma função parecida mas levemente diferente:
def soma2(a, b):
print(a + 2*b)
soma2(1,2)
x = 1
y = 2
soma2(x,y)
novamente, o argumento a
assume o valor de x
e o argumento b
assume o valor de y
.
Exercício 1¶
O que o programa a seguir vai imprimir?
a = 1
b = 2
soma2(a,b)
e o programa
a = 1
b = 2
soma2(b, a)
Tente explicar o que aconteceu.
Os dois exemplos acima são bem simples mas ainda são diferentes do que observamos com funções matemáticas:
x = soma(1,2)
# Esperamos que o valor de `x` seja 3 (ou não...)
x
A função pode retornar um valor. Então podemos reescrever a função soma
da seguinte maneira:
def soma(a,b):
return a+b
x = soma(1,2)
x
Agora temos algo que se comporta como as funções len
ou int
por exemplo.
Na verdade esse é o melhor jeito de se escrever uma função: usando os argumentos e o valor de retorno. A primeira versão é limitada. Não podemos usar diretamente o valor da soma.
Se a pessoa que está usando a função quiser imprimir o valor da soma, ela pode chamar a função e imprimir o valor. Muito mais útil e flexível:
print(soma(1,2))
x = soma(1,2)
print(x)
# Função para determinar se número é par
def é_par(x):
return x % 2 == 0 # Se o resto da divisão for zero, o número é par!
print(é_par(1))
print(é_par(2))
print(é_par(3))
print(é_par(4))
Uma função pode chamar outra!
def par_ou_ímpar(x):
if é_par(x):
return "par"
else:
return "ímpar"
print(par_ou_ímpar(5))
print(par_ou_ímpar(32))
Exercício 2¶
Escreva a função máximo
que retorna o maior de dois números
Exercício 3¶
Escreva a função múltiplo
que retorna True
se o primeiro argumento for um múltiplo do segundo
Exercício 4¶
Escreva a função área_quadrado
que recebe o lado de um quadrado e calcula a área.
Exercício 5¶
Escreve a função área_triângulo
que recebe a base e a altura de um triângulo e retorna a sua área
# Programa para pesquisar os elementos de uma lista:
def pesquise(lista, valor):
for i,e in enumerate(lista):
if e == valor:
return i
return None
pesquise([1,2,3], 2)
pesquise([1,2,3], 4)
pesquise([1,2,3], 4) == None
# Programa para somar os elementos de uma lista:
def soma(L):
total = 0
for v in L:
total += v
return total
soma([1,2,3])
# A média dos elementos de uma lista
def média(L):
total = 0
for v in L:
total += v
return total / len(L)
média([1,2,3])
Mas será que não dá para melhorar? A função é quase igual à função soma!
def média(L):
return soma(L) / len(L)
média([1,2,3])
Como não escrever a função soma
:
def soma_ruim(L):
total = 0
i = 0
while i < 5:
total += L[i]
i += 1
return total
soma_ruim([1,2,3,4,5])
# Porque isso dá errado???
soma_ruim([1,2,3,4,5,6,7])
Fatorial
O fatorial de um número inteiro positivo n
é dado por
e o fatorial de zero é 1: $$ 0! = 1 $$
def fatorial(n):
fat = 1
while n > 1:
fat *= n
n -= 1
return fat
fatorial(5)
Geralmente existem outras maneiras de escrever uma função:
def fatorial(n):
fat = 1
i = 1
while i <= n:
fat *= i
i += 1
return fat
fatorial(5)
Exercício 6¶
Reescreve a função para pesquisar uma lista (pesquise
) acima de modo a que use os métodos de pesquisa em lista que vimos na última aula.
Variáveis locais e globais¶
As funções que escrevemos até aqui tinham argumentos e eventualmente uma variável dentro da função. Que valor essa variável dentro da função assume? E se tivermos uma variável com mesmo nome fora da função?
EMPRESA = "Unidos Venceremos LTDA"
def imprime_cabeçalho():
print(EMPRESA)
print("-" * len(EMPRESA))
imprime_cabeçalho()
Uma variável global é definida fora da função. Mas é aqui que as podem ficar confusas.
a = 5
def teste(a):
print(a) # Que a estamos usando?
teste(10)
Este caso a gente já viu! Dentro da função estamos usando o argumento da função.
a = 5
def teste2():
print(a)
teste2()
Este caso também está claro: estamos usando a variável global.
E agora?
a = 5
def teste3():
a = 7
print(a)
teste3()
Beleza, nada inesperado. Mas agora que estamos fora da função, qual o valor de a
???
a
Esquisito né? O que aconteceu?
Ao executarmos a linha a=7
, criamos uma nova variável local!
Bom então no exemplo a seguir, vai imprimir 5 antes de mudar o valor (ou não???)
a = 5
def teste4():
print(a)
a = 7
teste4()
QUE É QUE ACONTECEU???? O PYTHON FICOU LOUCO
(ou não...)
O que ocorre é que existe uma atribuição de a
dentro da função. Isso automaticamente faze com que a
se torne uma variável local. Então a função nem sabe da existência da variável global a
e portanto dá erro!
Mas e se eu quiser modificar o valor da variável global a
? Use a instrução global
a = 5
def teste5():
global a # Olha o global aqui!
a = 7
print(f"Dentro da função: {a}")
print(f"Antes de chamar a função: {a}")
teste5()
print(f"Depois de chamar a função: {a}")
Recomendações¶
EVITE USAR VARIÁVEIS GLOBAIS. Quando você usa variáveis globais, o que uma função faz depende do estado do programa. Isso pode introduzir erros difíceis de serem diagnosticados. Se possível, passe o que seria uma variável global como um novo argumento da função.
Se você estiver usando variáveis globais, provavelmente você não pensou bem na estrutura do teu programa.
É lógico que isso não é dogma e variáveis globais podem ser úteis, por exemplo alguma configuração do programa.
Mas sempre pense duas (3, 4, 5) vezes antes de usar variáveis globais.
Funções recursivas¶
Já vimos que uma função pode chamar outra. Mas uma função pode chamar a si mesma! Esta é uma função recursiva.
Lembra da função fatorial? Bom, ela pode ser definida da seguinte maneira:
$$ 0! = 1 $$$$ 1! = 1 $$$$ n! = n \times (n-1)! $$Com essa definição,
def fatorial(n):
if n <= 1:
return 1
else:
return n * fatorial(n-1)
Repare que é importante que exista uma condição de parada! Caso contrário a recursão não parará e o programa será executado até ocorrer o erro RecursionError
.
Este erro ocorre pois a memória pode estourar (na verdade a pilha). Isso é equivalente ao loop infinito que já vimos para o while
mas nesse caso, pode não ocorrer um erro.
def fatorial_errado(n):
return n * fatorial_errado(n-1)
fatorial_errado(10)
# Vamos rastrear o fatorial?
def fatorial_rastreado(n):
print(f"Calculando o fatorial de {n}")
if n==0 or n==1:
print(f"Fatorial de {n} = 1")
return 1
else:
fat = n * fatorial_rastreado(n-1)
print(f" fatorial de {n} = {fat}")
return fat
fatorial_rastreado(4)
Sequência de Fibonacci
Se $n \le 1$,
fibonacci(n) = 1
caso contrário,
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(20)
Exercício 7¶
Defina uma função recursive que calcule o maior divisor comum (MDC) entre dois números a e b em que a > b.
mdc(a,b) = a
se b==0
e se a > b
, mdc(a,b) = mdc(b, a % b)
Exercício 8¶
Usando a função mdc
do exercício anterior, defina uma função para calcular o mínimo múltiplo comum (MMC) entre dois números:
Lembre que $|a \times b|$ é escrito como abs(a * b)
em Python.
Exercício 9¶
Reescreva o programa para calcular a sequência de Fibonacci sem utilizar recursão.
Compare o desempenho com a função recursiva. Se você estiver usando o notebook do jupyter ou o ipython você pode usar a instrução %time
como mostrado a seguir
%time fibonacci(10)
Pense no desempenho destas duas implementações. Consegue explicar?
Parâmetros opcionais¶
Algumas vezes, os parâmetros da função podem ter valores típicos que mudam pouco. Então podemos usar um parâmetro opcional. Este parâmetro opcional, se não for fornecido, terá um valor padrão, definido junto com a função.
# Função para desenhar uma barra
def barra():
return '*'*40
barra()
Mas eu quero especificar o número de caracteres:
def barra(n):
return '*' * n
print(barra(40)) # Equivalente ao primeiro
print(barra(60))
print(barra(20))
Mas e seu quiser usar um caracter diferente? Use um outro argumento!
def barra(n, caracter):
return caracter * n
print(barra(40, '*'))
print(barra(40, '='))
Mas agora eu preciso ficar digitando o caracter também ?!?
Use um argumento opcional!
def barra(n, caracter='*'):
return caracter * n
print(barra(40))
print(barra(40, '='))
Mas geralmente vou usar 40 caracteres!
Então use outro argumento opcional!
def barra(n=40, caracter='*'):
return caracter * n
print(barra())
print(barra(20))
print(barra(40, '-'))
print(barra(20, '-'))
Nomeando parâmetros¶
Python é ainda mais flexível ainda! Você pode usar os parâmetros pelo nome!
def retângulo(largura, altura, caracter):
linha = caracter * largura
for i in range(altura):
print(linha)
# Como fizemos até aqui.
retângulo(20, 4, '*')
# Podemos fazer isso:
retângulo(caracter='+', altura=3, largura=15)
E podemos combinar isso com is argumentos opcionais:
def retângulo(largura=20, altura=4, caracter='*'):
linha = caracter * largura
for i in range(altura):
print(linha)
retângulo()
retângulo(caracter='+')
retângulo(largura=40)
retângulo(largura=40, altura=10)
retângulo(largura=40, caracter='-', altura=10)
Isso é muito bom quando a função tem vários argumentos.
Funções como parâmetro¶
Até agora, os parâmetros das funções foram variáveis numéricas, listas, etc. Pode ser também uma função!
É isso mesmo. Uma função pode ser passada como um parâmetro em outra chamada de função!
def soma(a,b):
return a+b
def subtração(a,b):
return a-b
def multiplicação(a,b):
return a*b
def imprime(a, b, foper):
print(foper(a,b))
imprime(5,2,soma)
imprime(5,2,subtração)
imprime(5,2,multiplicação)
Parece esquisito. Será que tem uso?
Ahhhh, muito. Permite você flexibilizar o teu programa. Dar funcionalidade nova. Fazendo isso bem, o teu usuário vai poder usar as tuas funções de maneiras que você nunca imaginou.
def imprime_lista(L, fimpressão, fcondição):
for e in L:
if fcondição(e):
fimpressão(e)
def imprime_elemento(e):
print(f"Valor: {e}")
def épar(x):
return x % 2 == 0
def éímpar(x):
return not épar(x)
L = [1,7,9,2,11,0]
imprime_lista(L, imprime_elemento, épar)
imprime_lista(L, imprime_elemento, éímpar)
Exercício 10¶
Na aula 03 implementamos o algoritmo de reordenação de listas bubble sort. Crie uma função bubble_sort
que reordena os elementos de uma lista de números em ordem crescente
Exercício 11¶
Mude o programa do exercício 10 de modo que tenha um segundo parâmetro comparador
que é uma função que compara dois elementos
def maior(a, b):
return a > b
def bubble_sort(L, comparador=maior)
# Corpo da função
Esse segundo parâmetro é o que compara os elementos da lista. Se esse parâmetros for a função maior
, a lista será reordenada em ordem crescente.
Qual seria a função passada para o parâmetro comparador
para que a lista fosse reordenada em ordem decrescente?
Exercício 12¶
Escreva uma função mapa
que recebe como primeiro parâmetro uma função e segundo parâmetro uma lista e cria uma nova lista aplicando esta função em cada elemento da lista.
Exemplo:
python-repl
In [10]: def dobro(n):
...: return 2*n
...:
In [11]: mapa(dobro, [1,2,3])
Out[11]: [2, 4, 6]
List comprehension¶
No exercício 12, criamos uma função que aplica uma função a cada elemento de uma lista. Isso é tão útil que os criadores do Python implementaram uma sintaxe útil e simples para fazer isso: List Comprehension.
Com isso, muitas vezes o código fica mais curto e legível. Você nem precisa implementar laços e coisas parecidas.
L = [x for x in range(10)]
L
Z = [2*x for x in L]
Z
Dá para criar coisas mais complexas:
[(x, 2*x, x*x) for x in L]
É funciona com outros tipos de dados. Na verdade com qualquer tipo de dado indexável:
Z = [s.upper() for s in "abcdefg"]
Z
o melhor de tudo é que dá para filtrar as coisas
p = [x for x in range(10) if x % 2 == 0] # Só os pares
p
Tipo de uma variável. A função type
¶
Em Python todas as variáveis e objetos têm um tipo. Você pode inspecionar isso usando a função type
type(10)
type(1.23)
type("IPT")
type([1,2,3])
type({'a':1, 'b':2})
type({1,2,3,4})
type(len)
Quando você está escrevendo uma função, é útil verificar se um parâmetro tem um tipo específico. Para isso existe a função isinstance
que verifica se um objeto é de um tipo específico ou de uma lista de tipos:
isinstance(1, int)
isinstance(1, float)
isinstance(1, (float,int))
isinstance([1,2,3], list)
isinstance((1,2,3), tuple)
isinstance((1,2,3), list)
isinstance((1,2,3), (list, tuple))
isinstance([1,2,3], (list, tuple))
import types # Módulo da biblioteca padrão para tipos
def diz_o_tipo(a):
if isinstance(a, str):
return 'String'
elif isinstance(a, list):
return 'Lista'
elif isinstance(a, dict):
return 'Dicionário'
elif isinstance(a, int):
return 'Número inteiro'
elif isinstance(a, float):
return 'Número decimal'
elif isinstance(a, types.FunctionType):
return 'Função'
elif isinstance(a, types.BuiltinFunctionType):
return 'Função interna'
else:
return str(type(a))
diz_o_tipo(1)
diz_o_tipo([1,2,3])
diz_o_tipo(1.2)
diz_o_tipo(len)
diz_o_tipo(diz_o_tipo)
diz_o_tipo((1,2,3))
Exceções¶
Várias vezes ocorrem erros no programa. Neste curso fizemos alguns erros de propósto para mostrar funcionalidade e algumas vezes sem querer mesmo ao digitar algo errado.
Quando estes erros ocorreram, o programa simplesmente foi interrompido. Isso não é nem um pouco legal. Imagina você está usando um programa e de repente porque você digitou algo o programa acusa um erro e sai. Este não é o melhor dos mundos...
Em python um erro é conhecido como uma exceção (exception em inglês)
L = [1,2,3]
L[5]
int("IPT")
Repare que existem diferentes tipos de exceções!
# Capturando uma exceção:
nomes = ["Ana", "Carlos", "Maria"]
for tentativa in range(3):
try:
i = int(input("Digite o número que quer imprimir: "))
print(nomes[i])
break
except ValueError:
print("O programa só aceita número")
except IndexError:
print(f"Valor inválido, digite entre -3 e 3!")
# Outra possibilidade:
# Capturando uma exceção:
nomes = ["Ana", "Carlos", "Maria"]
for tentativa in range(3):
try:
i = int(input("Digite o número que quer imprimir: "))
print(nomes[i])
break
except Exception as e:
print(f"Algum erro na entrada: {e}")
Caso finalmente dê certo, existe a declaração finally
. Essa parte sempre é executada!
nomes = ["Ana", "Carlos", "Maria"]
for tentativa in range(3):
try:
i = int(input("Digite o número que quer imprimir: "))
print(nomes[i])
break
except Exception as e:
print(f"Algum erro na entrada: {e}")
finally:
print(f"Tentativa {tentativa+1}")
print("FIM")
math.sqrt(-1)
Módulos¶
Ao se fazer um programa, escrevem-se funções para se realizar diferentes coisas. Estas funções podem ser úteis em outros programas. Então é interessante reaproveitar o teu trabalho. Com o decorrer do tempo, se você for organizado, você pode ter uma biblioteca de funções que são extremamente úteis e economizam um tempo danado.
O mecanismo básico para isso é o módulo. O módulo é um arquivo com terminação .py
com funções (e outras coisas). Em geral está na mesma pasta que o programa que você está desenvolvendo. Como exemplo, vejamos o módulo utilidade.py
import os
os.getcwd()
import utilidades
utilidades.sim_ou_não("Sim ou Não?")
utilidades.entrada_float("Entre com um número: ")
Comentários
Comments powered by Disqus