P
PeterBraden1
Hi,
I am learning python, having learnt most of my object orientation with
java, and decided to port some of my geometry classes over. I haven't
used immutability in python before, so thought this would be an
interesting chance to learn.
I am looking for feedback on any ways in which I might have left
variables unprotected, and ways in which I am not being pythonic.
Cheers!
<code class = "python">
#!/usr/bin/env python
#
# geometry.py
#
# Peter Braden <http://PeterBraden.co.uk>
#
# Released under the GPLv2 (http://www.gnu.org/licenses/
gpl-2.0.txt).
#
# Disclaimer:
#
# All following code is provided "as is". Peter Braden hereby
disclaims
# solely to the extent permitted by law all express, implied and
statutory
# warranties, conditions and terms including, without limitation,
those
# regarding the security, reliability, timeliness, and performance of
the code.
#
#
import math
class Angle (object):
"""
The Angle class represents angles; the inclination to each other,
in a plane,
of two lines which meet each other, and do not lie straight with
respect to
each other.
* By default angles are measured in radians.
* Angles are immutable
"""
def __init__(self, val, type = 'rad'):
"""
Create a new angle.
* To specify type of value use either type = "deg" or type
="rad"
* default is radians
"""
if type == 'rad':
super(Angle, self).__setattr__('_value', val)
else:
super(Angle, self).__setattr__('_value',
math.radians(val))
def __eq__(self, other):
"""
Test equality
"""
if isinstance(other, Angle):
return self._value == other._value
return NotImplemented
def __ne__(self, other):
"""
Test Inequality
"""
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def __str__(self):
"""
Create Readable String
"""
return "%s (%s degrees)" % (str(self._value),
str(self.degrees))
def __repr__(self):
"""
Serialise data
"""
return "Angle(%s)" % self._value
def __setattr__(self, name, value):
"""
Suppress setting of data - Angle is immutable
"""
self._immutableError()
def __delattr__(self, name):
"""
Suppress deleting of data - Angle is immutable
"""
self._immutableError()
def __add__(self, other):
"""
return self + other
"""
if isinstance(other, Angle):
return Angle(self._value + other._value)
return NotImplemented
def __sub__(self, other):
"""
return self - other
"""
if isinstance(other, Angle):
return Angle(self._value - other._value)
return NotImplemented
def __mul__(self, other):
"""
return self * other
"""
if isinstance(other, Angle):
return Angle(self._value * other._value)
return NotImplemented
def __div__(self, other):
"""
return self / other
"""
if isinstance(other, Angle):
return Angle(self._value / other._value)
return NotImplemented
def __lt__(self, other):
"""
return self < other
"""
if isinstance(other, Angle):
return self._value < other._value
return NotImplemented
def __gt__(self, other):
"""
return self > other
"""
if isinstance(other, Angle):
return self._value > other._value
return NotImplemented
def __le__(self, other):
"""
return self >= other
"""
if isinstance(other, Angle):
return self._value <= other._value
return NotImplemented
def __ge__(self, other):
"""
return self <= other
"""
if isinstance(other, Angle):
return self._value >= other._value
return NotImplemented
def fromCos(self, c):
"""
return angle with specified cos
"""
return Angle(math.acos(c))
fromCos = classmethod(fromCos)
def fromSin(self, s):
"""
return angle with specified sin
"""
return Angle(math.asin(c))
fromSin = classmethod(fromSin)
def fromTan(self, t):
"""
return angle with specified tan
"""
return Angle(math.atan(c))
fromTan = classmethod(fromTan)
def _immutableError(self):
"""
Throw error about angles immutability
"""
raise TypeError("Angle is immutable - cannot alter variables")
radians = property(lambda self: self._value, lambda x:
self._immutableError())
degrees = property(lambda self: math.degrees(self._value), lambda
x: self._immutableError())
cos = property(lambda self: math.cos(self._value), lambda x:
self._immutableError())
sin = property(lambda self: math.sin(self._value), lambda x:
self._immutableError())
tan = property(lambda self: math.tan(self._value), lambda x:
self._immutableError())
def withinRange(self, angle, range):
"""
angle is within range of self
"""
return (self._value < angle._value + range) and (self._value >
angle._value - range)
def isAcute(self):
"""
angle is acute?
"""
return self._value < (math.pi/2)
#Common Values
DEG_30 = Angle(math.radians(30))
DEG_60 = Angle(math.radians(60))
DEG_90 = Angle(math.radians(90))
DEG_120 = Angle(math.radians(120))
DEG_180 = Angle(math.radians(180))
DEG_270 = Angle(math.radians(270))
DEG_360 = Angle(math.radians(360))
class Point2D (object):
"""
2 dimensional point type.
* Can represent both vectors and points
* Immutable
"""
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if isinstance(other, Point2D):
return self.x == other.x and self.y == other.y
return NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def __str__(self):
return "<%s, %s>" % (str(self.x), str(self.y))
def __repr__(self):
return "Point2D(%s, %s)" % (self.x, self.y)
def __setattr__(self, name, value):
self._immutableError()
def __delattr__(self, name):
self._immutableError()
def __add__(self, other):
if isinstance(other, Point2D):
return Point2D(self.x + other.x, self.y +other.y)
return NotImplemented
def __sub__(self, other):
if isinstance(other, Point2D):
return Point2D(self.x - other.x, self.y - other.y)
return NotImplemented
def __mul__(self, other):
if isinstance(other, Point2D):
return self.x*other.x + self.y*other.y)
return NotImplemented
def __getitem__(self,index):
if index == 0:
return self.x
if index == 1:
return self.y
raise TypeError("Index out of bounds for Point2D (x is 0, y is
1)")
def __setitem__(self,index,value):
self._immutableError()
def __delitem__(self,index,value):
self._immutableError()
def _immutableError(self):
raise TypeError("Point2D is immutable - cannot alter
variables")
length = property(lambda self: return math.sqrt(self.x*self.x +
self.y*self.y), lambda x: self._immutableError())
def normalise(self):
return Point2D(self.x/self.length, self.y/self.length);
def translate(self, other) {
return self + other
}
def rotate(self, pivot, rot) {
return Point2D( ((self.x - pivot.x) * rot.cos - (self.y -
pivot.y)* rot.sin) + pivot.x,
((self.x - pivot.x)*rot.sin + (self.y -
pivot.y)*rot.cos)+pivot.y)
}
def dotProduct(self, c){
return self * c
}
def crossProductLength(self, p){
return (self.x * p.y - self.y * p.x);
}
</code>
I am learning python, having learnt most of my object orientation with
java, and decided to port some of my geometry classes over. I haven't
used immutability in python before, so thought this would be an
interesting chance to learn.
I am looking for feedback on any ways in which I might have left
variables unprotected, and ways in which I am not being pythonic.
Cheers!
<code class = "python">
#!/usr/bin/env python
#
# geometry.py
#
# Peter Braden <http://PeterBraden.co.uk>
#
# Released under the GPLv2 (http://www.gnu.org/licenses/
gpl-2.0.txt).
#
# Disclaimer:
#
# All following code is provided "as is". Peter Braden hereby
disclaims
# solely to the extent permitted by law all express, implied and
statutory
# warranties, conditions and terms including, without limitation,
those
# regarding the security, reliability, timeliness, and performance of
the code.
#
#
import math
class Angle (object):
"""
The Angle class represents angles; the inclination to each other,
in a plane,
of two lines which meet each other, and do not lie straight with
respect to
each other.
* By default angles are measured in radians.
* Angles are immutable
"""
def __init__(self, val, type = 'rad'):
"""
Create a new angle.
* To specify type of value use either type = "deg" or type
="rad"
* default is radians
"""
if type == 'rad':
super(Angle, self).__setattr__('_value', val)
else:
super(Angle, self).__setattr__('_value',
math.radians(val))
def __eq__(self, other):
"""
Test equality
"""
if isinstance(other, Angle):
return self._value == other._value
return NotImplemented
def __ne__(self, other):
"""
Test Inequality
"""
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def __str__(self):
"""
Create Readable String
"""
return "%s (%s degrees)" % (str(self._value),
str(self.degrees))
def __repr__(self):
"""
Serialise data
"""
return "Angle(%s)" % self._value
def __setattr__(self, name, value):
"""
Suppress setting of data - Angle is immutable
"""
self._immutableError()
def __delattr__(self, name):
"""
Suppress deleting of data - Angle is immutable
"""
self._immutableError()
def __add__(self, other):
"""
return self + other
"""
if isinstance(other, Angle):
return Angle(self._value + other._value)
return NotImplemented
def __sub__(self, other):
"""
return self - other
"""
if isinstance(other, Angle):
return Angle(self._value - other._value)
return NotImplemented
def __mul__(self, other):
"""
return self * other
"""
if isinstance(other, Angle):
return Angle(self._value * other._value)
return NotImplemented
def __div__(self, other):
"""
return self / other
"""
if isinstance(other, Angle):
return Angle(self._value / other._value)
return NotImplemented
def __lt__(self, other):
"""
return self < other
"""
if isinstance(other, Angle):
return self._value < other._value
return NotImplemented
def __gt__(self, other):
"""
return self > other
"""
if isinstance(other, Angle):
return self._value > other._value
return NotImplemented
def __le__(self, other):
"""
return self >= other
"""
if isinstance(other, Angle):
return self._value <= other._value
return NotImplemented
def __ge__(self, other):
"""
return self <= other
"""
if isinstance(other, Angle):
return self._value >= other._value
return NotImplemented
def fromCos(self, c):
"""
return angle with specified cos
"""
return Angle(math.acos(c))
fromCos = classmethod(fromCos)
def fromSin(self, s):
"""
return angle with specified sin
"""
return Angle(math.asin(c))
fromSin = classmethod(fromSin)
def fromTan(self, t):
"""
return angle with specified tan
"""
return Angle(math.atan(c))
fromTan = classmethod(fromTan)
def _immutableError(self):
"""
Throw error about angles immutability
"""
raise TypeError("Angle is immutable - cannot alter variables")
radians = property(lambda self: self._value, lambda x:
self._immutableError())
degrees = property(lambda self: math.degrees(self._value), lambda
x: self._immutableError())
cos = property(lambda self: math.cos(self._value), lambda x:
self._immutableError())
sin = property(lambda self: math.sin(self._value), lambda x:
self._immutableError())
tan = property(lambda self: math.tan(self._value), lambda x:
self._immutableError())
def withinRange(self, angle, range):
"""
angle is within range of self
"""
return (self._value < angle._value + range) and (self._value >
angle._value - range)
def isAcute(self):
"""
angle is acute?
"""
return self._value < (math.pi/2)
#Common Values
DEG_30 = Angle(math.radians(30))
DEG_60 = Angle(math.radians(60))
DEG_90 = Angle(math.radians(90))
DEG_120 = Angle(math.radians(120))
DEG_180 = Angle(math.radians(180))
DEG_270 = Angle(math.radians(270))
DEG_360 = Angle(math.radians(360))
class Point2D (object):
"""
2 dimensional point type.
* Can represent both vectors and points
* Immutable
"""
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if isinstance(other, Point2D):
return self.x == other.x and self.y == other.y
return NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def __str__(self):
return "<%s, %s>" % (str(self.x), str(self.y))
def __repr__(self):
return "Point2D(%s, %s)" % (self.x, self.y)
def __setattr__(self, name, value):
self._immutableError()
def __delattr__(self, name):
self._immutableError()
def __add__(self, other):
if isinstance(other, Point2D):
return Point2D(self.x + other.x, self.y +other.y)
return NotImplemented
def __sub__(self, other):
if isinstance(other, Point2D):
return Point2D(self.x - other.x, self.y - other.y)
return NotImplemented
def __mul__(self, other):
if isinstance(other, Point2D):
return self.x*other.x + self.y*other.y)
return NotImplemented
def __getitem__(self,index):
if index == 0:
return self.x
if index == 1:
return self.y
raise TypeError("Index out of bounds for Point2D (x is 0, y is
1)")
def __setitem__(self,index,value):
self._immutableError()
def __delitem__(self,index,value):
self._immutableError()
def _immutableError(self):
raise TypeError("Point2D is immutable - cannot alter
variables")
length = property(lambda self: return math.sqrt(self.x*self.x +
self.y*self.y), lambda x: self._immutableError())
def normalise(self):
return Point2D(self.x/self.length, self.y/self.length);
def translate(self, other) {
return self + other
}
def rotate(self, pivot, rot) {
return Point2D( ((self.x - pivot.x) * rot.cos - (self.y -
pivot.y)* rot.sin) + pivot.x,
((self.x - pivot.x)*rot.sin + (self.y -
pivot.y)*rot.cos)+pivot.y)
}
def dotProduct(self, c){
return self * c
}
def crossProductLength(self, p){
return (self.x * p.y - self.y * p.x);
}
</code>