Módulos
Fazendo os nossos próprios módulos
Nós já usamos módulos como o math
e o sys
:
import math
r = math.log(6) ## chama a função log no módulo math
import sys
## acessa a lista de argumentos da linha de comendo no módulo sys
x = eval(sys.argv[1])
Características dos módulos:
- Coleção de dados úteis e funções (também classes);
- Funções em um módulo podem ser reutilizadas em vários diferentes programas;
- Se você tem algumas funções gerais que podem ser utilizadas em mais de um programa, faça um módulo com essas funções.
- É fácil: apenas colete as funções que você quer em um arquivo e pronto, temos um módulo!
Fazendo nosso próprio módulo
Aqui estão algumas formulas para cálculo com taxas de juros:
Sendo $ A_0 $: montante inicial, $ p $: taxa, $ n $: dias, $ A $: montante final.
Queremos fazer um módulo com essas equações.
Primeiro fazemos funções Python com as equações
import math
def montante_final(A0, p, n):
return A0*(1 + p/(360.0*100))**n
def montante_inicial(A, p, n):
return A*(1 + p/(360.0*100))**(-n)
def dias(A0, A, p):
return math.log(A/A0)/math.log(1 + p/(360.0*100))
def taxa_anual(A0, A, n):
return 360*100*((A/A0)**(1.0/n) - 1)
Então podemos fazer o módulo
Salve as 4 funções em um arquivo taxas_juros.py
Pronto! Agora taxas_juros.py é um módulo.
Exemplo de uso:
import taxas_juros as tj
A0 = 1
A = 2
p = 5
n = tj.dias(A0, 2, p)
anos = n/365.0
print 'O dinheiro dobrou após %.1f anos' % anos
Adicionando um bloco de teste em um módulo
- Módulos podem ter um teste
if
no final contendo um bloco de teste para testar ou demonstrar o funcionamento do próprio módulo; - O bloco de teste não é executado quando o arquivo é importado como um módulo em um programa;
- O bloco de teste é executado somente quando o arquivo roda como um programa!
Sintaxe básica:
if __name__ == '__main__': ## esse teste define o bloco de teste
<block of statements>
No nosso caso, acrescenta-se logo após as funções:
if __name__ == '__main__':
A = 2.2133983053266699
A0 = 2.0
p = 5
n = 730
print 'A=%g (%g) A0=%g (%.1f) n=%d (%d) p=%g (%.1f)' % \
(montante_final(A0, p, n), A,
montante_inicial(A, p, n), A0,
dias(A0, A, p), n,
taxa_anual(A0, A, n), p)
Blocos de teste normalmente coletados em funções
Vamos fazer uma função de teste real para testar o que temos no bloco de teste:
def teste_todas_funcoes():
## Definindo valores compatíveis
A = 2.2133983053266699
A0 = 2.0
p = 5
n = 730
## Dados três desses valores, calcule o remanescente
## e compare com o valor correto (em parênteses)
A_calculado = montante_final(A0, p, n)
A0_calculado = montante_inicial(A, p, n)
n_calculado = dias(A0, A, p)
p_calculado = taxa_anual(A0, A, n)
def float_eq(a, b, tolerancia=1E-12):
"""Retorne True if a == b dentro da tolerância."""
return abs(a - b) < tolerancia
success = float_eq(A_calculado, A) and \
float_eq(A0_calculado, A0) and \
float_eq(p_calculado, p) and \
float_eq(n_calculado, n)
assert success ## pode-se adicionar uma mensagem aqui se desejado
if __name__ == '__main__':
teste_todas_funcoes()
Como o Python pode encontrar nosso novo módulo?
- Se o módulo está no mesmo diretório do programa principal, ele é importado sem problema algum;
-
Módulos por nós confeccionados são normalmente colecionados em um diretório comum, por exemplo:
/home/usuario/Documentos/meusModulos
-
Nesse caso o Python precisa ser avisado que seus módulos estão naquele diretório!
Técnica 1: adicione o diretório ao PYTHONPATH
no arquivo .rc
, no caso de sistemas Linux:
export PYTHONPATH=$PYTHONPATH:/home/usuario/Documentos/meusModulos
Técnica 2: adicione o diretório ao sys.path
no programa:
sys.path.insert(0, '/home/usuario/Documentos/meusModulos')
Técnica 3: copie o arquivo do módulo para um diretório o qual o Python já busca por módulos.
Tarefa
Resolução de $f(x)=0$ para equações não lineares utilizando método da bisecção.
Equações não lineares para serem resolvidas:
O método da bisecção:
- Inicie com um intervalo $ [a,b] $ no qual $ f(x) $ muda de sinal;
- Então deve haver ao menos uma raiz em $ [a,b] $;
- Reduza o intervalo à metade:
- $ m=(a+b)/2 $; faz $ f $ mudar de sinal na metade da esquerda $ [a,m] $?
- Sim: continue com o intervalo da esquerda $ [a,m] $ (faça $ b=m $);
- Não: continue com o intervalo da direita $ [m,b] $ (faça $ a=m $).
- $ m=(a+b)/2 $; faz $ f $ mudar de sinal na metade da esquerda $ [a,m] $?
-
Repita o procedimento!
-
Após reduzir o intervalo inicial à metade $ [p,q] $ $n$ vezes, sabemos que $ f(x) $ deve ter uma raiz dentro de um intervalo (pequeno) $ 2^{-n}(q-p) $;
- O método é lento, mas muito confiável.
- Outros métodos (como Newton-Raphson) podem ser mais rápidos, mas podem também falhar ao localizar a raiz, o método da bisecção não falha.
Como fazer:
- Precisa-se transcrever a descrição matemática do método da bisecção para um programa Python;
- Um passo intermediário importante é formular um algoritmo preciso!
Como escolher $n$? Ou seja, quando parar a iteração.
- Queremos que o erro na raiz seja $ \epsilon $ (tolerância) ou menor;
- Após $ n $ iterações, o intervalo inicial $ [a,b] $ é dividido à metade $ n $ vezes e o intervalo atual tem tamanho $ 2^{-n}(b-a) $. Isso é suficientemente pequeno se $ 2^{-n}(b-a) = \epsilon \quad \Rightarrow \quad n = - \dfrac{ \ln \epsilon - \ln (b-a)}{ \ln 2} $;
- Uma alternativa simples: apenas repita a divisão à metade até que o comprimento do intervalo atual seja $ \leq \epsilon $;
- Podemos também adicionar um teste para verificar se $ f $ realmente muda de sinal no intervalo inicial $ [a,b] $.
Opcional: Deixando o algoritmo mais eficiente
- $ f(a) $ é recalculada a cada teste
if
; - Não é necessário se $ a $ não mudou de sinal desde o último passo no laço;
- Em computadores modernos e com fórmulas simples para $ f(x) $ esses cálculos extras não importam;
- No entanto, na ciência e na engenharia existem funções $ f $ que levam horas e até dias para serem avaliadas em um ponto, então salvar alguma avaliação de $ f(a) $ importa!
- Regra geral: remova cálculos redundantes
(a não ser que o código se torne muito complicado e difícil de verificar)
Regras à cumprir:
- Passe todos os dados necessários, função, limites e tolerância via argumentos de linha de comando com opções (
argparse
); - O método da bisecção deve ser implementado como uma função em um módulo à parte!
- Faça ao menos uma função teste para o método da bisecção;
- Limitação: use somente o aprendido até aqui!