Ir para o conteúdo

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 $).
  • 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:

  1. Passe todos os dados necessários, função, limites e tolerância via argumentos de linha de comando com opções (argparse);
  2. O método da bisecção deve ser implementado como uma função em um módulo à parte!
  3. Faça ao menos uma função teste para o método da bisecção;
  4. Limitação: use somente o aprendido até aqui!