Goal 1 - Lazy Evalution
import math
class Polygon:
def __init__(self, n, R):
if n < 3:
raise ValueError('Polygon must have at least 3 vertices.')
self._n = n
self._R = R
self._interior_angle = None
self._side_length = None
self._apothem = None
self._area = None
self._perimeter = None
def __repr__(self):
return f'Polygon(n={self._n}, R={self._R})'
@property
def count_vertices(self):
return self._n
@property
def count_edges(self):
return self._n
@property
def circumradius(self):
return self._R
@property
def interior_angle(self):
if self._interior_angle is None:
self._interior_angle = (self._n - 2) * 180 / self._n
return self._interior_angle
@property
def side_length(self):
if self._side_length is None:
self._side_length = 2 * self._R * math.sin(math.pi / self._n)
return self._side_length
@property
def apothem(self):
if self._apothem is None:
self._apothem = self._R * math.cos(math.pi / self._n)
return self._apothem
@property
def area(self):
if self._area is None:
print('calculate area')
self._area = self._n / 2 * self.side_length * self.apothem
return self._area
@property
def perimeter(self):
if self._perimeter is None:
self._perimeter = self._n * self.side_length
return self._perimeter
def __eq__(self, other):
if isinstance(other, self.__class__):
return (self.count_edges == other.count_edges
and self.circumradius == other.circumradius)
else:
return NotImplemented
def __gt__(self, other):
if isinstance(other, self.__class__):
return self.count_vertices > other.count_vertices
else:
return NotImplemented
p = Polygon(5, 3)
print(p.area)
# calculate area
# 21.398771616640957
print(p.area)
# 21.398771616640957
print(p.area)
# 21.398771616640957
def test_polygon():
abs_tol = 0.001
rel_tol = 0.001
try:
p = Polygon(2, 10)
assert False, ('Creating a Polygon with 2 sides: '
' Exception expected, not received')
except ValueError:
pass
n = 3
R = 1
p = Polygon(n, R)
assert str(p) == 'Polygon(n=3, R=1)', f'actual: {str(p)}'
assert p.count_vertices == n, (f'actual: {p.count_vertices},'
f' expected: {n}')
assert p.count_edges == n, f'actual: {p.count_edges}, expected: {n}'
assert p.circumradius == R, f'actual: {p.circumradius}, expected: {n}'
assert p.interior_angle == 60, (f'actual: {p.interior_angle},'
' expected: 60')
n = 4
R = 1
p = Polygon(n, R)
assert p.interior_angle == 90, (f'actual: {p.interior_angle}, '
' expected: 90')
assert math.isclose(p.area, 2,
rel_tol=abs_tol,
abs_tol=abs_tol), (f'actual: {p.area},'
' expected: 2.0')
assert math.isclose(p.side_length, math.sqrt(2),
rel_tol=rel_tol,
abs_tol=abs_tol), (f'actual: {p.side_length},'
f' expected: {math.sqrt(2)}')
assert math.isclose(p.perimeter, 4 * math.sqrt(2),
rel_tol=rel_tol,
abs_tol=abs_tol), (f'actual: {p.perimeter},'
f' expected: {4 * math.sqrt(2)}')
assert math.isclose(p.apothem, 0.707,
rel_tol=rel_tol,
abs_tol=abs_tol), (f'actual: {p.perimeter},'
' expected: 0.707')
p = Polygon(6, 2)
assert math.isclose(p.side_length, 2,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.apothem, 1.73205,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.area, 10.3923,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.perimeter, 12,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.interior_angle, 120,
rel_tol=rel_tol, abs_tol=abs_tol)
p = Polygon(12, 3)
assert math.isclose(p.side_length, 1.55291,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.apothem, 2.89778,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.area, 27,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.perimeter, 18.635,
rel_tol=rel_tol, abs_tol=abs_tol)
assert math.isclose(p.interior_angle, 150,
rel_tol=rel_tol, abs_tol=abs_tol)
p1 = Polygon(3, 10)
p2 = Polygon(10, 10)
p3 = Polygon(15, 10)
p4 = Polygon(15, 100)
p5 = Polygon(15, 100)
test_polygon()
Goal 2 - iterable
class Polygons:
def __init__(self, m, R):
if m < 3:
raise ValueError('m must be greater than 3')
self._m = m
self._R = R
self._max_efficiency_polygon = None
def __repr__(self):
return f'Polygons(m={self._m}, R={self._R})'
def __iter__(self):
return self.PolyIterator(m=self._m, R=self._R)
class PolyIterator:
def __init__(self, m, R):
if m < 3:
raise ValueError('m must be greater than 3')
self._i = 3
self._m = m
self._R = R
def __iter__(self):
return self
def __next__(self):
if self._i > self._m:
raise StopIteration
else:
result = Polygon(self._i, self._R)
self._i += 1
return result
@ property
def max_efficiency_polygon(self):
if self._max_efficiency_polygon is None:
sorted_polygons = sorted(self.PolyIterator(self._m, self._R),
key=lambda p: p.area/p.perimeter,
reverse=True)
self._max_efficiency_polygon = sorted_polygons[0]
return self._max_efficiency_polygon
p_iter = iter(Polygons(5, 3))
print(next(p_iter))
# Polygon(n=3, R=3)
print(next(p_iter))
# Polygon(n=4, R=3)
print(next(p_iter))
# Polygon(n=5, R=3)
print(next(p_iter))
# StopIteration
polygons = Polygons(5, 3)
for i in polygons:
print(i)
# Polygon(n=3, R=3)
# Polygon(n=4, R=3)
# Polygon(n=5, R=3)
print(polygons.max_efficiency_polygon)
# Polygon(n=5, R=3)