Re: [Edu-sig] Design patterns
> -Original Message- > From: Kirby Urner [mailto:[EMAIL PROTECTED] > > A lot of these lessons about robust software development come from group > or > community efforts. Some aspects of Python maybe don't much excite you > because you're primarily a solo coder (as am I much of the time). I guess. Though I can't say I find there to be much consensus out there about what language features truly make for robust software development from group or community efforts. Python itself can be thought of as a robust community effort - in C, which itself probably pre-dates/ignores much of the lessons of what is necessary for such effort. Java seems obsessed with these kinds of concerns - which is why I do in fact find coding in it as a solo as a kind of foolishness. OTOH, I find code readability absolutely satisfying, even without an expectation that anyone else will in fact be reading it. Whatever. Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> Why do I find this unsatisfying? > I think part of the picture is you're coding for yourself i.e. your APIs are used internally by your own code. Refactoring *everything* is always a possibility. Suppose you'd already published a Triangle class API and then discovered you needed more dynamism. The property feature lets you sneak in some methods without changing the already-published API and breaking client code. A lot of these lessons about robust software development come from group or community efforts. Some aspects of Python maybe don't much excite you because you're primarily a solo coder (as am I much of the time). Kirby ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> Though when we add another dimension, all tetras are projectively > equivalent. Part of why I can't adjust to a focus on a tetra that happens > to be regular in some way. > > Art > Well, another way of putting it is: all tetrahedra are the same regular one, except not because of viewpoints. Kirby ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
Arthur wrote: > > As an API matter, I find myself liking the clue that "()" provides as to > whether one is accessing something designed be determined dynamically. In general I agree with that sentiment. > I find myself leaning towards the option of making the use of properties go > away in PyGeo. If you want to keep anything as properties, I'd keep the ones those properties with the least dynamic nature. --Scott David Daniels [EMAIL PROTECTED] ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> -Original Message- > From: Kirby Urner [mailto:[EMAIL PROTECTED] > > Good point about all triangles being equivalent given projection. In > nailing down the angles, we've inadvertently defined a fourth vertex: the > point of view. Given we're talking four vertices, we should maybe rename > our class Tetrahedron ;-D. Good solution - as it leaves open the question as to where is the triangle and where is the point of view ;) Though when we add another dimension, all tetras are projectively equivalent. Part of why I can't adjust to a focus on a tetra that happens to be regular in some way. Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> PyGeo has a Triangle object, inherited from the Plane object. It exists > mostly for drawing purposes, as the portion of the plane enclosed by the > infinite lines connecting any 3 points. Since all Triangles are > projectively equivalent, in the context of PyGeo there is little to be > gained by saying much of anything about any particular triangle - and > little > is. > > > Art Good point about all triangles being equivalent given projection. In nailing down the angles, we've inadvertently defined a fourth vertex: the point of view. Given we're talking four vertices, we should maybe rename our class Tetrahedron ;-D. A transformation that has no impact on angles, is scaling. A feature I'd like to add to the Triangle sub/class is scalability -- using operator overloading why not? Here a design question is: should mytri * 3 change mytri, or should it return a new object. My preference is for the latter, since we can always rebind mytri to the output of __mul__. Should I also define __div__ while I'm at it? class Triangle2(BaseTriangle): def _reset(self): self.perimeter = self.a + self.b + self.c s = 0.5 * self.perimeter self.area = math.sqrt(s * (s - self.a) *(s - self.b) * (s - self.c)) self.A = math.acos((-self.a**2 + self.b**2 + self.c**2) / (2.0 * self.b * self.c)) self.B = math.acos((self.a**2 - self.b**2 + self.c**2) / (2.0 * self.a * self.c)) self.C = math.acos((self.a**2 + self.b**2 - self.c**2) / (2.0 * self.a * self.b)) def __mul__(self, scalar): a = self.a * scalar b = self.b * scalar c = self.c * scalar return Triangle2(a,b,c) __rmul__ = __mul__ >>> reload(trig) >>> from trig import Triangle2 as Tri >>> mytri = Tri(3,4,5) >>> mytri = mytri * 3 >>> mytri.area 54.0 >>> mytri.a 9 >>> math.degrees(mytri.C) 90.0 Other enhancements: I could add xyz coordinates as read-only, but have translation, rotation and scale methods (this last already defined) make them change. Having xyz coordinates will allow me to feed triangle objects to draw objects, e.g. for output to VPython, POV-Ray, VRML or what-have-we. Kirby ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> -Original Message- > From: Kirby Urner [mailto:[EMAIL PROTECTED] > A weakness in the above design: we only check for violations of triangle > inequality in the constructor, yet allow changes to a,b,c through the API. Among my list of unsupportable theories is one to the effect that any attempt to build a truly OOP hierarchy of geometric objects requires one to get to geometric fundamentals and that requires one to get down to projective ideas. Apparently there is in OOP theory a nearly irresolvable paradox of the relation of the circle to an ellipse in a OOP hierarchy. But from a projective point of view they are only the same object, viewed from different perspectives, i.e. they are projectively equivalent. So attempting to distinguish them as different levels of some hierarchy is bound to be problematic. It's really a geometric problem disguised as a programming one. IOW, attempting to fit these objects into an OOP hierarchy chain can only be an attempt to fit a round ellipse into a square circle, or vice versa ;) PyGeo has a Triangle object, inherited from the Plane object. It exists mostly for drawing purposes, as the portion of the plane enclosed by the infinite lines connecting any 3 points. Since all Triangles are projectively equivalent, in the context of PyGeo there is little to be gained by saying much of anything about any particular triangle - and little is. Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> -Original Message- > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On > Behalf Of Scott David Daniels > > I'd propose two reasons why properties are so successful. > > The first explanation comes from eXtreme Programming. One big goal of > XP is to stop wasting time doing "big design up front." Then my situation is a bit ironic. What I don't seem to have realized - at least to my own satisfaction - is some rational scheme for the use of properties. They are used - somewhere in the range from haphazardly to intuitively. So I am left to refactor working code to give some scheme to my use of properties. And since this refactoring is coming at a fairly late stage of design, I don't feel the same need to leave my XP options open to the extent that I might had I thought more thoroughly about the use of properties earlier in the process. So where I come out on the use of properties now might not be the same as where I would have come out at some different point. Why do I find this unsatisfying? As an API matter, I find myself liking the clue that "()" provides as to whether one is accessing something designed be determined dynamically. So I find myself leaning towards the option of making the use of properties go away in PyGeo. Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
Scott: > def writeprop(viewvar): > '''decorator makes a property from access and a write method''' > def view(self): > return getattr(self, viewvar) > def buildprop(writemethod): > return property(view, writemethod) > return buildprop > Pretty fancy Scott and again, highly worthy of study. An element in this design pattern is using a function (writeprop) to build the decorator function (buildprop) that'll transform the following def (e.g. def c). Scott used the same technique when he enhanced my hypertoon program (a VPython cartoon generator) with decorator syntax. @f1(args) def f2: pass uses f1(args) to build a function which then takes f2 as its argument, rebinding the result to f2, i.e. f2 = (f1(args))(f2), where f1(args) by itself returns a callable. In this case, buildprop returns a curried property function (curried in that the 'view' method is already bound, defined in terms of getattr). The decorator returns a function the effect of which is equivalent to 'c = property(view, c)' i.e. c-the-function goes in as buildprop's writemethod and is rebound to make c a property. > @writeprop('_c') > def c(self, v): > self.check(v, self.a, self.b) > self._c = v > self._reset() Here's a similar base triangle class without the decorators: class BaseTriangle2(object): @classmethod def check(klass, a, b, c): '''Check three lengths for suitability as triangle sides''' if a >= b + c: raise ValueError, 'Too long: %s >= %s + %s' % (a, b, c) if a <= abs(b - c): raise ValueError, 'Too short: %s <= abs(%s - %s)' % (a, b,c) def __init__(self, a, b, c): self.check(a, b, c) self._a = a self._b = b self._c = c self._reset() def __repr__(self): return '%s(%s, %s, %s)' % (self.__class__.__name__, self.a, self.b, self.c) def _reset(self): '''Called whenever the sides of the triangle change''' pass def _seta(self, v): self.check(v, self.b, self.c) # fixed typo in original self._a = v self._reset() def _setb(self, v): self.check(v, self.a, self.c) self._b = v self._reset() def _setc(self, v): self.check(v, self.a, self.b) self._c = v self._reset() def _geta(self): return self._a def _getb(self): return self._b def _getc(self): return self._c a = property(_geta, _seta) b = property(_getb, _setb) c = property(_getc, _setc) Kirby ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
Arthur wrote: >>What beyond sugar for leaving off a "()" when trying to retrieve a value >>from a method are we accomplishing by using properties? I have tended to >>look at properties mostly an accommodation to those coming from other >>languages which have something similar, but never as something that was >>core >>to Python or a Pythonic approach to things. Am I missing something >>fundamental? > > > The searching I do on this point only confirms to me that my own confusion > is well shared - perhaps indicating this to be an area not totally OT for > this forum. > > Ray Hettinger's "How-To Guide for Descriptors" > > http://users.rcn.com/python/download/Descriptor.htm > > covers properties, but the use case given is extremely unsatisfying - > essentially offering a situation where a fundamental design change has been > made to a program in mid-stream, and since properties were used in the > initial design, the change can be made with little refactoring. I'd propose two reasons why properties are so successful. The first explanation comes from eXtreme Programming. One big goal of XP is to stop wasting time doing "big design up front." I've spent enough time in companies where long design meetings (months) produced huge design documents. The documents themselves had a tendency to look like everything was nailed down, while not really answering a lot of questions that had to be solved when writing the code. The length of time spent producing the document, and the general unavailability of the group that wrote it (they typically moved into other committees to write other design documents), led to an increasingly rigid design that reflected neither discoveries or innovations from the coders nor changes in requirements (or at least our understanding of those requirements). XP can be seen as a reaction to that problem. The second explanation is much lower level. In O-O code, there is a distinction between interface and implementation. Essentially, the interface to an object is how the object behaves from a user of that object's point of view. The implementation is how that object accomplishes its behavior. When you separate these concerns, you can more likely keep a programs complexity (in the sense of debugging/ extending) from growing exponentially with the number of lines of code. Properties let you to hide the trade-off between accessing a stored value and computing it on the fly. Without properties, the interface to classes that want to reserve the access/calculate tradeoff must use the Java-like "getVar" functions to fetch any values that might be calculated. > What would it take to create a @property decorator that allows one > to set as well as get? Would we want to? def writeprop(viewvar): '''decorator makes a property from access and a write method''' def view(self): return getattr(self, viewvar) def buildprop(writemethod): return property(view, writemethod) return buildprop Triangle as an example: import math class BaseTriangle(object): @classmethod def check(klass, a, b, c): '''Check three lengths for suitability as triangle sides''' if a >= b + c: raise ValueError, 'Too long: %s >= %s + %s' % (a, b, c) if a <= abs(b - c): raise ValueError, 'Too short: %s <= abs(%s - %s)' % (a, b,c) def __init__(self, a, b, c): self.check(a, b, c) self._a = a self._b = b self._c = c self._reset() def __repr__(self): return '%s(%s, %s, %s)' % (self.__class__.__name__, self.a, self.b, self.c) def _reset(self): '''Called whenever the sides of the triangle change''' pass @writeprop('_a') def a(self, v): self.check(v, self.b + self.c) self._a = v self._reset() @writeprop('_b') def b(self, v): self.check(v, self.a, self.c) self._b = v self._reset() @writeprop('_c') def c(self, v): self.check(v, self.a, self.b) self._c = v self._reset() # One kind of triangle with angles: class Triangle(BaseTriangle): @property def perimeter(self): return self.a + self.b + self.c @property def area(self): "Heron's Formula" s = 0.5 * self.perimeter return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c)) @property def A(self): return math.acos((-self.a**2 + self.b**2 + self.c**2) / (2.0 * self.b * self.c)) @property def B(self): return math.acos((self.a**2 - self.b**2 + self.c**2) / (2.0 * self.a * self.c)) @property def C(self): return math.acos((self.a**2 + self.b**2 - self.c**2) / (2.0 * self.a * self.b)) # Another kind of triangle with angles: class Triangle2(BaseTriangle
Re: [Edu-sig] Design patterns
Hi Arthur -- Yes, decorator syntax isn't designed to integrate with properties quite the same way. All a decorator does is run the subsequently defined function f2 through the f1 in @f1, rebinding f2 to the returned output of f1. E.g. 'f2 = f1(f2)' and '@f1; def f2(): pass' amount to the same thing. The non-decorator version of Triangle, with properties, might be: >>> import math >>> class Triangle(object): def __init__(self, a,b,c): "Construct a triangle" if (c >= a + b) or (a >= b + c) or (b >= a + c): raise ValueError("Illegal edges: %s/%s/%s" % (a, b, c)) self.a = a self.b = b self.c = c def getArea(self): "Heron's Formula" s = 0.5 * (self.a + self.b + self.c) return math.sqrt( s * (s - self.a) * (s - self.b) * (s - self.c)) def getA(self): return math.acos((-self.a**2 + self.b**2 + self.c**2) / (2.0 * self.b * self.c)) def getB(self): return math.acos((self.a**2 - self.b**2 + self.c**2) / (2.0 * self.a * self.c)) def getC(self): return math.acos((self.a**2 + self.b**2 - self.c**2) / (2.0 * self.a * self.b)) A = property(getA, doc='Angle A in radians') B = property(getB, doc='Angle B in radians') C = property(getC, doc='Angle C in radians') area = property(getArea, doc="Triangle's area") >>> mytri = Triangle(3,4,5) >>> math.degrees(mytri.C) 90.0 >>> math.degrees(mytri.A) 36.86989764584402 >>> mytri.area = 6 Traceback (most recent call last): File "", line 1, in -toplevel- mytri.area = 6 AttributeError: can't set attribute Alex Martelli's example in 'Python in a Nutshell' is similar: a rectangle's area is computed on the fly (pg. 85). Note that defining attributes in this way doesn't mean we only find out an attribute is really a property much further along in the code (everything is expressed in one line) -- which was the problem decorators addressed i.e. a long function def might obscure the fact that we're defining a classmethod (the older f1 = classmethod(f1) had to come at the end). >>> help(Triangle) # now get us this useful section in the documentation: | -- | Properties defined here: | | A | Angle A in radians | | = getA(self) | | B | Angle B in radians | | = getB(self) | | C | Angle C in radians | | = getC(self) | | area | Triangle's area | | = getArea(self) | Heron's Formula | | -- I think it's fine to call it "syntactic sugar" when we make object attributes call their associated accessors and mutators behind the scenes. Conceptually, we're giving the user of the class a simpler API while giving ourselves as programmers a lot of control over what triggers what. It's easy to think of angles and area as attributes, even if we don't allow them to be set except through changes to edges. Why should we force a user to remember what's a method and what's not, given edges, angles and area (could add perimeter too). A weakness in the above design: we only check for violations of triangle inequality in the constructor, yet allow changes to a,b,c through the API. Kirby ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> -Original Message- > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On > Behalf Of Arthur > > What beyond sugar for leaving off a "()" when trying to retrieve a value > from a method are we accomplishing by using properties? I have tended to > look at properties mostly an accommodation to those coming from other > languages which have something similar, but never as something that was > core > to Python or a Pythonic approach to things. Am I missing something > fundamental? The searching I do on this point only confirms to me that my own confusion is well shared - perhaps indicating this to be an area not totally OT for this forum. Ray Hettinger's "How-To Guide for Descriptors" http://users.rcn.com/python/download/Descriptor.htm covers properties, but the use case given is extremely unsatisfying - essentially offering a situation where a fundamental design change has been made to a program in mid-stream, and since properties were used in the initial design, the change can be made with little refactoring. Somehow I don't expect a programming language to accommodate the possibility of a midstream change in a programs design/intentions of the kind cited here. New design, and the expectation would be to need to recode to the new design. All within bounds, of course. OOP, I think, does try to address this issue to a reasonable degree. But the properties use case in this article is, to my intuition, a stretch beyond those reasonable bounds. I can't believe that a language or coding style intended to accommodate the range of possibilities for the kind of fundamental midstream redesign referenced in this use case could lead anywhere else but to some kind of madness. The fact is that I do use properties to a limited degree in PyGeo - the actual implementation of properties being trivial enough. Much less trivial is the why and when. The brain dead use of properties in PyGeo being definitely on my list for refactoring. Just not sure whether I should be consistent in avoiding them, or consistently using them, or when is which and which is when. Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig
Re: [Edu-sig] Design patterns
> -Original Message- > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On > Behalf Of Scott David Daniels > > Here's how to do the angles in 2.4 w/ properties: > > > @property > def A(self): > return math.acos((-self.a**2 + self.b**2 + self.c**2) >/ (2.0 * self.b * self.c)) Hmmm... We seem to be saying that @property works out-of-the box as a decorator in 2.4 - at least where we want read only properties. I can only assume that this is more of a side-effect of the introduction of decorators, than a feature fully designed into the language. Which is not to say that it is not a fortunate side effect. But it is unfortunate combo for me - since I never fully understood the purpose of properties, and don't fully get the mechanics of decorators. My most important points of confusion: What beyond sugar for leaving off a "()" when trying to retrieve a value from a method are we accomplishing by using properties? I have tended to look at properties mostly an accommodation to those coming from other languages which have something similar, but never as something that was core to Python or a Pythonic approach to things. Am I missing something fundamental? and Am I correct that the use of property as a "built-in" decorator is not designed into the language - at least in the same way that @classmethod has been? What would it take to create a @property decorator that allows one to set as well as get? Would we want to? Art ___ Edu-sig mailing list [EMAIL PROTECTED] http://mail.python.org/mailman/listinfo/edu-sig