Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como criar uma classe para retângulos e triângulos no python

0 votos
47 visitas
perguntada Ago 16 em Ciência da Computação por Alan Antunes Rosendo (26 pontos)  

Criar uma classe como a classe "Circle" da seção 7.2.3 para representar outras figuras geométricas: um retângulo com lado W, altura H e vértice inferior esquerdo \((x_0,y_0)\); e um triângulo especificado por três vértices \((x_0,y_0),(x_1,y_1),(x_2,y_2)\). Implemente três funções: init, area e perímetro.
Implemente funções teste testRectangle() e testTriangle() para verificar se os resultados produzidos pelos comandos area e perímetro coincidem com os valores exatos com uma pequena tolerância.

Exercício 7.4 do Cap. 7 do livro "A Primer on Scientific Programming with Python" de Hans Petter Langtangen, 5ª edição.

Compartilhe

1 Resposta

0 votos
respondida Ago 16 por Alan Antunes Rosendo (26 pontos)  

Para criar as classes, implementei o seguinte código:

class rectangle(object):
   def __init__(self,x0,y0,W,H):
     self.x0, self.y0, self.W, self.H=x0,y0,W,H

class triangle(object):
   def __init__(self,x1,y1,x2,y2,x3,y3):
     self.x1, self.y1,self.x2, self.y2,self.x3, self.y3 =x1,y1,x2,y2,x3,y3

Com as classes criadas, criei as funções de área e de perímetro de cada elemento (as funções tem que ficar dentro da sua classe).

A implementação delas é bem simples, no caso do retângulo foi acrescentado o seguinte código:

    def area(self):
        return self.W*self.H

    def perimeter(self):
        return 2*self.W+2*self.H

Já no caso do triângulo, foi utilizado o seguinte código:

def area(self):
    x1,y1,x2,y2,x3,y3=self.x1,self.y1,self.x2,self.y2,self.x3,self.y3
    return 1/2*(x2*y3-x3*y2-x1*y3+x3*y1+x1*y2-x2*y1)

def perimeter(self):
    x1,y1,x2,y2,x3,y3=self.x1,self.y1,self.x2,self.y2,self.x3,self.y3
    import math
    s3=math.sqrt((x3-x2)**2+(y3-y2)**2)
    s2=math.sqrt((x2-x1)**2+(y2-y1)**2)
    s1=math.sqrt((x3-x1)**2+(y3-y1)**2)
    return s1+s2+s3

Após essa implementação, foi necessário fazer a função de teste para cada classe.
Essa função consiste em calcular as variáveis com as informações do modelo e comparar com o valor encontrado ao se utilizar a função criada anteriormente. A diferença entre esses valores deve ser menor que a tolerância.

As funções das duas classes ficaram assim:

  def test_rectangle():
    w,h=2,3
    r=rectangle(0,0,w,h)

    expected_area=w*h
    computed_area=r.area()
    diff=abs(expected_area-computed_area)
    tol=1E-14
    assert diff<tol, 'bug in rectangle.area, diff=%s' %diff

    expected_perimeter=2*w+2*h
    computed_perimeter=r.perimeter()
    diff=abs(expected_perimeter-computed_perimeter)
    assert diff<tol, 'bug in rectangle.perimeter, diff=%s' %diff

  def test_triangle():
    x1,y1,x2,y2,x3,y3=0,0,1,0,0,2
    t=triangle(x1,y1,x2,y2,x3,y3)

    expected_area=1/2*(x2*y3-x3*y2-x1*y3+x3*y1+x1*y2-x2*y1)
    computed_area=t.area()
    diff=abs(expected_area-computed_area)
    tol=1E-14
    assert diff<tol, 'bug in triangle.area, diff=%s' %diff

    import math
    expected_perimeter=math.sqrt((x3-x2)**2+(x2-x1)**2+(x3-x1)**2+
                                 (y3-y2)**2+(y2-y1)**2+(y3-y1)**2)
    computed_perimeter=t.perimeter()
    diff=abs(expected_perimeter-computed_perimeter)
    assert diff<tol, 'bug in triangle.perimeter, diff=%s' %diff

Para testar se a implementação ficou correta, foi acrescentado o seguinte trecho:

if __name__=='__main__':
r1=rectangle(0,0,2,3)
print('A rectangle with sides %g and %g has area %g and perimeter %g'
      %(r1.W, r1.H, r1.area(), r1.perimeter()))

t1=triangle(0,0,1,0,0,2)
print('A triangle with vertex (%g,%g),(%g,%g),(%g,%g) has area %g and perimeter %g'
      %(t1.x1, t1.y1,t1.x2,t1.y2,t1.x3,t1.y3,t1.area(), t1.perimeter()))

Dessa forma a execução do programa ficou separada do código, facilitando a visualização.

O código completo, na ordem implementada ficou assim:

class rectangle(object):
def __init__(self,x0,y0,W,H):
    self.x0, self.y0, self.W, self.H=x0,y0,W,H

def area(self):
    return self.W*self.H

def perimeter(self):
    return 2*self.W+2*self.H

def test_rectangle():
    w,h=2,3
    r=rectangle(0,0,w,h)

    expected_area=w*h
    computed_area=r.area()
    diff=abs(expected_area-computed_area)
    tol=1E-14
    assert diff<tol, 'bug in rectangle.area, diff=%s' %diff

    expected_perimeter=2*w+2*h
    computed_perimeter=r.perimeter()
    diff=abs(expected_perimeter-computed_perimeter)
    assert diff<tol, 'bug in rectangle.perimeter, diff=%s' %diff
class triangle(object):
def __init__(self,x1,y1,x2,y2,x3,y3):
    self.x1, self.y1,self.x2, self.y2,self.x3, self.y3 =x1,y1,x2,y2,x3,y3

def area(self):
    x1,y1,x2,y2,x3,y3=self.x1,self.y1,self.x2,self.y2,self.x3,self.y3
    return 1/2*(x2*y3-x3*y2-x1*y3+x3*y1+x1*y2-x2*y1)

def perimeter(self):
    x1,y1,x2,y2,x3,y3=self.x1,self.y1,self.x2,self.y2,self.x3,self.y3
    import math
    s3=math.sqrt((x3-x2)**2+(y3-y2)**2)
    s2=math.sqrt((x2-x1)**2+(y2-y1)**2)
    s1=math.sqrt((x3-x1)**2+(y3-y1)**2)
    return s1+s2+s3

def test_triangle():
    x1,y1,x2,y2,x3,y3=0,0,1,0,0,2
    t=triangle(x1,y1,x2,y2,x3,y3)

    expected_area=1/2*(x2*y3-x3*y2-x1*y3+x3*y1+x1*y2-x2*y1)
    computed_area=t.area()
    diff=abs(expected_area-computed_area)
    tol=1E-14
    assert diff<tol, 'bug in triangle.area, diff=%s' %diff

    import math
    expected_perimeter=math.sqrt((x3-x2)**2+(x2-x1)**2+(x3-x1)**2+
                                 (y3-y2)**2+(y2-y1)**2+(y3-y1)**2)
    computed_perimeter=t.perimeter()
    diff=abs(expected_perimeter-computed_perimeter)
    assert diff<tol, 'bug in triangle.perimeter, diff=%s' %diff
if __name__=='__main__':
r1=rectangle(0,0,2,3)
print('A rectangle with sides %g and %g has area %g and perimeter %g'
      %(r1.W, r1.H, r1.area(), r1.perimeter()))

t1=triangle(0,0,1,0,0,2)
print('A triangle with vertex (%g,%g),(%g,%g),(%g,%g) has area %g and perimeter %g'
      %(t1.x1, t1.y1,t1.x2,t1.y2,t1.x3,t1.y3,t1.area(), t1.perimeter()))
comentou Ago 23 por Fabio Fujita (36 pontos)  
editado Ago 27 por Fabio Fujita
Olá Alan.

Parabéns pela solução. Seguem alguns comentários e sugestões.

1. INDENTAÇÃO: Creio que quando o código completo foi copiado para o Prorum (ao final de sua solução) a indentação foi modificada. Por exemplo, note que as definições das funções dentro das classes estão no mesmo nível da própria classe (deveriam estar recuadas em um nível). O mesmo ocorre dentro do programa principal. Como o Python é sensível à indentação, foi necessário ajustar para poder rodar o código.

2. FÓRMULA PARA ÁREA DO TRIÂNGULO E A FUNÇÃO DE TESTE: é necessário fazer um pequeno ajuste na fórmula da área do triângulo que você implementou, extraindo o valor absoluto. Note que, dependendo dos vértices que você escolhe, seu código está calculando uma área negativa, o que é impossível. Teste, por exemplo, o triângulo de vértices (0,1), (0,2) e (1,1), que tem área igual a 0,5, sendo obtida a resposta de -0,5 pelo código por não estar extraindo o módulo.
O erro não é detectado na função de teste pois ela está realizando o cálculo com a mesma fórmula implementada na função de cálculo da área.

3. FÓRMULA PARA O PERÍMETRO DO TRIÂNGULO NA FUNÇÃO DE TESTE: A função de teste do perímetro do triângulo está indicando um erro acima da tolerância. Note que é necessário corrigir a fórmula utilizada para cálculo do perímetro esperado. O perímetro é calculado corretamente na função perimeter, ou seja:

        s3=math.sqrt((x3-x2)**2+(y3-y2)**2)
        s2=math.sqrt((x2-x1)**2+(y2-y1)**2)
        s1=math.sqrt((x3-x1)**2+(y3-y1)**2)
        return s1+s2+s3

No entanto, a fórmula da função "test_triangle()" tem um erro, extraindo a raiz quadrada de tudo:

expected_perimeter=math.sqrt((x3-x2)**2+(x2-x1)**2+(x3-x1)**2+
                                 (y3-y2)**2+(y2-y1)**2+(y3-y1)**2)

Os resultados são diferentes e a função aponta isso, conforme esperado.


4. IMPORTAÇÃO DA BIBLIOTECA MATH: a biblioteca "math" está sendo importada duas vezes dentro da classe “triangle”. Apesar de funcionar, seria interessante realizar uma única importação no início do código, antes das definições das classes, o que facilitaria também a leitura. Caso sua preocupação seja no caso de algum usuário querer importar a classe que você criou já levando a importação da biblioteca, também seria possível, mas creio que não seja necessário.

5. VERIFICAÇÃO DE ERROS DO USUÁRIO: não são realizados testes para verificação de erros que podem ser cometidos pelos usuários. Entre os possíveis erros, podemos citar retângulos com altura ou largura negativa (o que não é possível e poderia levar ao cálculo de áreas ou perímetros negativos) ou, no caso dos triângulos, a entrada de três vértices colineares (que não formam um triângulo). Como é apenas um exercício, talvez não seja necessário realizar uma verificação exaustiva dos erros que podem ser cometidos pelos usuários (assim como nos exemplos de aula), mas seria interessante colocar um comentário na sua solução ou no próprio corpo do código de que essas verificações não estão sendo realizadas.
...