Ao final dessa aula, você deve ser capaz de construir classes e escrever programas utilizando objetos dessas classes.
- Método com parâmetros com valores default.
- Métodos especiais para operadores comuns como ‘+’, ‘-‘, ‘*’, e ‘/’.
Vimos que um número harmônico \(H_n\) de um inteiro \(n > 0\) é dado por
Vamos fazer 2 implementações:
- DE - da direita para a esquerda e
- ED - da esquerda para direita.
Qual dessas formas é estável?
Observe que para \(n = 10\), os resultados são iguais, mas para \(n = 100 \ldots\)
A razão da diferença é que ambas as somas são aproximadas mas, ao somar os números “grandes” primeiro, a precisão da soma devido a grande quantidade de números “pequenos” é perdida. Portanto o resultado da soma DE (de 1/n a 1) é mais preciso que da soma ED.
Nesse exercício, vamos criar uma classe de números fracionários que permita realizar a soma sem perda de precisão.
Definição da classe `Racional`
Escreva uma classe Racional que representa um número racional, que pode ser expresso por um numerador e um denominador inteiros. Essa classe deve ter ao menos os seguintes métodos:
- add (para adição de racionais)
- sub (para subtração)
- mul (para multiplicação)
- div (para divisão)
Como um número racional é definido por seu numerador e denominador, precisamos de ao menos dois atributos para representá-los. Esses atributos podem ser carregados quando criamos um racional. Além disso, podemos também imaginar como será a representação textual de um racional, que pode ser o numerador seguido pelo denominador, separados por /.
Até agora a classe Racional seria:
Em muitos casos, é útil criar um objeto com valores pré-determinados, ou “default”. Por exemplo, podemos definir que o valor default de um Racional seja 0/1 (por que 0/0 seria uma má ideia?). Nesse caso, basta modificar o cabeçalho do método construtor __init__ da seguinte forma:
Observe que agora o valor default de n é 0 (parâmetro correspondente ao numerador) e de d é 1. Devido aos valores default, podemos definir os valores de n e d apenas quando são diferentes de 0 e 1 respectivamente. Nesse caso, observe que o valor do racional r1 é 0/1, o de r2 é 4/1 e o de r3, que recebeu os 2 argumentos, é 3/2. Quando apenas 1 argumento é passado, a ordem dos parâmetros se torna importante. Assim, na criação do racional r2, o segundo parâmetro (correspondente ao denominador) não foi fornecido e assume portanto o seu valor default 1.
Mas e se eu quiser passar apenas o valor do denominador?
Nesse caso, o Python permite que você passe os parâmetros usando os nomes definidos no cabeçalho do método. Assim:
`r4 = Racional(d=-1)`
e até:
`r5 = Racional(d=3, n=2)`
são chamadas válidas.
É possível ainda misturar parâmetros com e sem valores default. Se você decidir por misturar, procure agrupar os parâmetros com valores default no final do cabeçalho, para deixar as chamadas mais consistentes, como:
`def metodo(self, a, b, c = 0, d = 1):`
Experimente colocar outros parâmetros no __init__ (e crie outros métodos!) usando o trecho de código abaixo:
Polimorfismo
o uso de valores default é permitido também no cabeçalho de funções comuns (não apenas métodos) do Python.
Esse recurso é conhecido como polimorfismo, visto que permite que uma mesma função seja chamada de formas diferentes (com um número diferente de argumentos).
E para alterar os valores do numerador e denominador criados com os valores default?
Nesse caso, poderíamos simplesmente alterar os atributos diretamente
r1 = Racional()
r1.num, r1.den = 2, 3
Obviamente nesse caso seria mais simples escrever r1 = Racional(2,3). Em orientação à objetos também é comum usar um método para ler e escrever os valores de atributos, como os métodos put e get:
Operações com Racionais
As operações de multiplicação e divisão são relativamente simples. Na multiplicação basta multiplicar os numerodores e os denominadores, e na divisão basta multiplicar o primeiro pelo inverso do segundo racional.
No caso da adição e subtração, é necessário transformar os racionais para um mesmo denominador, antes de somar e subtrair os numeradores.
No exercício abaixo, escreva os métodos div, add e sub (para divisão, adição e subtração), e alguns testes. Antes de escrever os seus métodos, observe o código do método mul que preserva os estados dos objetos self e other, e retorna um outro (nova instância) racional. Faça o mesmo para div, add e sub.
Altere a classe Racional para que, quando o denominador for 1, ele imprima apenas o valor do numerador.
Altere o construtor da classe Racional para que, quando o numerador e o denominador possam ser reduzidos, a fração reduzida seja armazenada.
Exemplo: para a chamada Racional(12, 20) o valor de self.num e self.den devem ser, respectivamente, 3 e 5.
Dica: crie uma função (ou, se preferir, um método) com mdc que recebe 2 números inteiros a e b e retorna o máximo divisor comum entre a e b usando o algoritmo de Euclides.
Para tornar a classe Racional mais interessante, é possível modificar o comportamento dos símbolos comumente utilizados para adição, subtração, divisão e multiplicação (+, -, /, *) para que possamos escrever programas usando expressões como
r1 = Racional(2,3)
r2 = Racional(1,4)
r3 = Racional(3,5)
r4 = r1 + r2 * r3
Para isso, ao invés de escrever os métodos add, sub etc, podemos os seguintes métodos especiais:
- __add__: para adição (+)
- __sub__: para subtração (-)
- __mul__: para multiplicação (*)
- __truediv__: para divisão (/)
Altere a classe Racional para utilizar esses métodos ao invés de add, sub, etc (provavelmente, basta modificar o nome dos métodos apropriados), e mofifique os testes para utilizar esses operadores.
Para saber sobre esses e outros métodos mágicos, acesse esse tutorial.
O programa a seguir calcula o harmônico de um dado número inteiro \(n\) utilizando a soma ED (da esquerda para a direita). Rode o programa e verifique se há diferença entre os métodos usando a soma de números reais e a soma de números racionais.
Modifique o programa para incluir o cálculo da direita para a esquerda (DE).
OBSERVAÇÃO: Caso você encontre problemas ao executar esse código no seu navegador, copie o código para um editor como o IDLE3 e execute o programa em seu computador.
Escreva uma classe Ponto3D que representa um ponto no espaço tridimensional, com as seguintes operações:
- distância à origem
- distância entre dois pontos
- ponto médio entre dois pontos
- __str__ que devolve um string
Nesse caso, os atributos podem ser 3 reais representando as coordenadas do ponto.