Re: [Edu-sig] Design patterns

2005-08-21 Thread Arthur


> -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

2005-08-21 Thread Kirby Urner
> 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

2005-08-21 Thread Kirby Urner
> 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

2005-08-21 Thread Scott David Daniels
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

2005-08-21 Thread Arthur


> -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

2005-08-21 Thread Kirby Urner
> 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

2005-08-21 Thread Arthur


> -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

2005-08-21 Thread Arthur


> -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

2005-08-21 Thread Kirby Urner
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

2005-08-21 Thread Scott David Daniels
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

2005-08-21 Thread Kirby Urner
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

2005-08-21 Thread Arthur


> -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

2005-08-21 Thread Arthur


> -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