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> -- http://mail.python.org/mailman/listinfo/python-list