Funções são como liquidificadores! Inserimos bananas e um copo de leite, ajustamos alguns parâmetros como o tempo de processamento e por fim recebemos uma deliciosa vitamina! E o melhor é que não precisamos entender os mínimos detalhes sobre como os mecanismos internos do aparelho funcionam para fazer nossa vitamina. Tampouco precisamos ter construído o liquidificador para poder saboreá-la. Além disso, qualquer que fosse o liquidificador, esperaríamos obter algo não muito diferente de uma vitamina, desde que utilizássemos os mesmos ingredientes e regulagens.
Agora de forma mais sofisticada, funções são construções que encapsulam um determinado comportamento que se espera executar múltiplas vezes durante a execução de um programa. São rotinas, que podem ter sido definidas pelo próprio programador ou por outros programadores. No caso do liquidificador, a rotina foi definida pelo próprio fabricante. O usuário só precisa saber o que precisa saber como operá-lo: que tipos de coisas deve fornecer como entrada (input) e o que deve esperar receber como saída (output).
Funções facilitam sua vida por dois motivos principais.
Permitem ao programador abstrair computações, sem precisar se preocupar a todo momento sobre os mínimos detalhes de como elas são de fato realizadas. Imagine se você tivesse que se preocupar com os detalhes sobre como um texto é imprimido na tela de seu computador toda vez que você precisasse desta funcionalidade... Felizmente a função print
, que utilizamos no nosso programa "Hello World", permite que esta rotina seja abstraída para você, o que facilita bastante seu aprendizado!
Permitem compartilhar e reutilizar código. Se você construir uma função que possa ajudar outras pessoas também, por que não compartilhar? Isso acontece bastante na comunidade de programadores, e os ajuda a não ficar "reinventando a roda" quando precisam de alguma funcionalidade que já foi implementada por alguém.
Para começar a entender como trabalhar com funções, precisamos conhecer seus três componentes principais: (i) nome, (ii) parâmetros e (iii) corpo.
Dar um nome às funções é uma forma simples de mantermos uma referência a elas. Embora possamos nomear funções conforme nossa vontade, é recomendável escolhermos nomes que nos digam algo sobre seu funcionamento. É fácil lembrar que a função print
, por exemplo, serve para imprimir algo na tela.
Os parâmetros fornecem um meio para "afinarmos" o comportamento de uma função para nossas necessidades. Por exemplo, no caso do liquidificador, alguns parâmetros relevantes seriam o tempo de processamento, ou a velocidade de rotação do motor. Os dados de entrada (inputs) são também passados para as funções através de parâmetros. No jargão da programação, nos referimos aos valores que passamos no lugar de cada um dos parâmetros como argumentos. Pense em um parâmetro como um placeholder para um valor, enquanto o argumento é o valor em si, passado para dentro da função através de um parâmetro.
Por fim, no corpo da função especificamos todas as etapas que devem ser realizadas por ela. Estas etapas incluem o processamento dos dados de entrada (inputs) e a construção do resultado que ela produzirá como saída (output). No fim das contas, a ideia é que as computações descritas no corpo da função sejam abstraídas para o usuário da função.
Após esta aula você deverá ser capaz de:
Para definir uma nova função precisamos obedecer à seguinte sintaxe:
def
indica que uma nova função está sendo definida;def
, deve ser escrito o nome da função;:
após os parênteses indica que o corpo da função vem a seguir, no bloco de código abaixo;return
.Para ilustrar, vamos construir uma nova função, batizada com o nome foo
(poderia ser qualquer outro nome, tipo dinossauro
). Ela simplesmente deve imprimir no console os valores que passamos em cada um dos parâmetros e, no fim, retornar como output o valor $1$.
def foo(par1, par2):
print(par1)
print(par2)
return 1
Uma vez definida, a função fica guardada na memória, mas não é automaticamente executada. Para de fato executá-la, precisamos chamá-la, escrevendo seu nome e passando argumentos no lugar dos parâmetros, entre parênteses.
foo("Santos", "Dumont")
Santos Dumont
1
Vamos definir agora uma função chamada funcaoSoma
, que simplesmente soma dois números e retorna o resultado. Ela possui os parâmetros n1
e n2
, que esperam receber como valores (ou argumentos) dois valores numéricos.
def funcaoSoma(n1,n2):
res = n1 + n2
return res
funcaoSoma(3,5)
8
funcaoSoma(-3,23)
20
funcaoSoma(42,0)
42
Podemos também armazenar o resultado de funções em variáveis! Vamos declarar as variáveis a
, b
, c
e d
, com quaisquer valores numéricos. Em seguida, usaremos a função funcaoSoma
para somar a
e b
e armazene o resultado em uma variável a_b
. Depois, usaremos novamente a função funcaoSoma
para somar c
e d
e armazene o resultado em uma variável c_d
. Finalmente, somaremos os valores em a_b
e c_d
usando a mesma funcaoSoma
, armazenando o resultado final em uma variável resFinal
.
a = 3
b = 5
c = 9
d = 11
a_b = funcaoSoma(a,b)
c_d = funcaoSoma(c,d)
resFinal = funcaoSoma( a_b, c_d )
resFinal
28
Podemos também passar resultados de funções como argumentos para outras funções, sem precisar utilizar variáveis intermediárias! Podemos, por exemplo, realizar as mesmas computações da célula acima sem precisar declarar a_b
e c_d
.
a = 3
b = 5
c = 9
d = 11
resFinal = funcaoSoma( funcaoSoma(a,b), funcaoSoma(c,d) )
resFinal
28
Funções operam em um "contexto" próprio. É como se criássemos uma "bolha" toda vez que executamos uma função. Tudo o que está dentro desta bolha não é visível de fora dela, mas todo o conteúdo que está fora dela é visível de dentro. Nos referimos ao contexto de "dentro da bolha" como o escopo local da função, e tudo o que está fora como o escopo global.
Sendo assim, uma funçao "enxerga" todas as variáveis em escopo local, ou seja, aquelas que são definidas dentro dela própria (incluindo os parâmetros); e as variáveis em escopo global, definidas fora dela. No entanto, variáveis locais, definidas dentro de funções, não são visíveis de fora delas!
a = 3
print("Variável a ANTES da execução da função:",a)
b = 5
print("Variável b ANTES da execução da função:",b)
def foo(): # A função é apenas definida aqui
a = 7
print("Variável a DENTRO da função foo:",a)
print("Variável b DENTRO da função foo:",b)
m = 11
print("Variável m DENTRO da função foo:",m)
print('---')
foo() # A função apenas é de fato executada aqui
print('---')
print("Variável a APÓS a execução da função",a)
print("Variável b APÓS da execução da função:",b)
print("Variável m",m)
Variável a ANTES da execução da função: 3 Variável b ANTES da execução da função: 5 --- Variável a DENTRO da função foo: 7 Variável b DENTRO da função foo: 5 Variável m DENTRO da função foo: 11 --- Variável a APÓS a execução da função 3 Variável b APÓS da execução da função: 5
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-9-961615b014f8> in <module>() 18 print("Variável a APÓS a execução da função",a) 19 print("Variável b APÓS da execução da função:",b) ---> 20 print("Variável m",m) NameError: name 'm' is not defined
Obs: Um mesmo nome pode ser dado para variáveis em escopos diferentes (que podem conter valores diferentes)! Nestes casos, a variável que foi definida em escopo local prevalece sobre a global.
O segredo para um bom desempenho como programador é jamais reinventar a roda! Sempre que possível, devemos buscar reutilizar código que já foi escrito por outros programadores. Pacotes são uma forma eficiente usada pelos programadores para estruturar e compartilhar seu código com a comunidade, sendo criados com o intuito de entregar aos usuários um conjunto de funcionalidades. O código dentro de um pacote é organizado em módulos, cada qual contendo um conjunto de funções e variáveis que implementam funcionalidades mais específicas.
Veja uma lista de pacotes potencialmente interessantes para um biólogo:
Em Python, existem algumas funções que são carregadas com a inicialização do interpretador, por serem utilizadas de forma mais ampla pelos programadores. Alguns exemplos são as funções print
, type
, range
e open
.
Outras, no entanto, são mais específicas e portanto não são carregadas automaticamente.
Usamos a palavra import
para carregar um pacote ou módulo.
O módulo math
, por exemplo, reúne um conjunto de funções e constantes que facilitam muito trabalhar com elementos da matemática. Estas funções não são carregadas automaticamente, e portanto devemos importar o módulo para utilizá-las.
Vamos carregar o módulo math
e, em seguida, executar a função factorial
provida por ele. Conforme podemos consultar na documentação, esta função espera um número como input e retorna seu fatorial, como output.
import math
print("Fatorial de 1:",math.factorial(1))
print("Fatorial de 2:",math.factorial(2))
print("Fatorial de 3:",math.factorial(3))
print("Fatorial de 10:",math.factorial(10))
Fatorial de 1: 1 Fatorial de 2: 2 Fatorial de 3: 6 Fatorial de 10: 3628800
Ex 1. Defina uma função calculaMedia
, que calcula a média entre três números quaisquer. Em seguida, execute sua função com valores diferentes. O que acontece se você definir a função com três parâmetros mas passar um número diferente de argumentos?
def calculaMedia(n1,n2,n3):
res = (n1+n2+n3)/3
return res
calculaMedia(1,3,5)
3.0
calculaMedia(1,2,5)
2.6666666666666665
# Este código vai gerar um erro: A função esperava três argumentos mas nós só passamos 2
calculaMedia(1,5)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-14-e266eef7ddd9> in <module>() 1 # Este código vai gerar um erro: A função esperava três argumentos mas nós só passamos 2 ----> 2 calculaMedia(1,5) TypeError: calculaMedia() missing 1 required positional argument: 'n3'
Ex 2. Defina uma função multiplica
, que multiplica dois números quaisquer. Em seguida, declare as variáveis a
, b
e c
com quaisquer valores numéricos. Multiplique a
e b
utilizando a função multiplica
e, em seguida, execute novamente a função multiplica
para multiplicar o resultado do passo anterior pelo número em c
.
def multiplica(n1,n2):
return n1*n2
a=3
b=5
c=7
multiplica( multiplica(a,b), c)
105
Ex 3. A função exponencia
abaixo deve realizar uma exponenciação do número n1
à potência n2
. Mas ao executar a célula para definir a função recebemos uma mensagem de erro. Corrija o código e experimente executar a função com alguns pares de números.
def exponencia(n1,n2):
return n1**n2
exponencia(2,3)
8
exponencia(5,2)
25
Ex 4. A função mensagemMeuNome
abaixo deveria receber como argumento o seu nome, imprimir uma mensagem com ele e, por fim, retornar o seu nome como output. No entanto, existe um bug. Você consegue identificá-lo e corrigí-lo?
nome = "Luke Skywalker"
def mensagemMeuNome( meuNome ):
print("Olá! Me chamo", meuNome)
return meuNome
mensagemMeuNome("Pedro") # insira seu nome entre parênteses
Olá! Me chamo Pedro
'Pedro'
Ex 5. A função type
é automaticamente carregada com a inicialização do interpretador Python. Você consegue descobrir o que ela faz? Experimente passar diferentes tipos de dados.
# A função type retorna o tipo do valor passado como argumento
print("Tipo de 'Algum texto':", type("Algum texto") )
print( "Tipo de 43:",type(43) )
print( "Tipo de True:", type(True) )
print( "Tipo de 32.21:", type(32.21) )
Tipo de 'Algum texto': <class 'str'> Tipo de 43: <class 'int'> Tipo de True: <class 'bool'> Tipo de 32.21: <class 'float'>