Instances' __setitem__ methods

2011-06-20 Thread Spencer Pearson
I was recently trying to implement a dict-like object which would do
some fancy stuff when it was modified, and found that overriding the
__setitem__ method of an instance did not act the way I expected. The
help documentation (from help(dict.__setitem__)) claims that
"d.__setitem__(k,v)" is equivalent to "d[k]=v", but I've produced this
code that, on Python 2.6, acts differently in the two cases.

def print_args( key, value ):
print "print_args called: key = %s, value = %s" %(key,value)

class MyDict( dict ):
def __init__( self ):
dict.__init__( self )
self.__setitem__ = print_args

def __setitem__( self, key, value ):
print "ModelDict.__setitem__ called"
dict.__setitem__( self, key, value )

d = MyDict()

print "d.__setitem__(0,1):",
d.__setitem__(0,1)

print "d[0]=1:",
d[0]=1


I would expect the two setitems to both call print_args, but that's
not what happens. In the first case, it calls print_args, but in the
second case, the __setitem__ declared in MyDict is called instead.

The documentation at 
http://docs.python.org/reference/datamodel.html#specialnames
says that for new-style classes, "x[i]" is equivalent to
"type(x).__getitem__(x, i)". I assume that "x[i]=y" has similarly been
changed to be equivalent to "type(x).__setitem__(x, i, y)", since that
would produce the results that I'm getting. Is the help documentation
for dict.__setitem__ just outdated, or am I missing some subtlety
here?

Also: when I say "d.f(*args)", am I correct in thinking that d checks
to see if it has an instance attribute called "f", and if it does,
calls f(*args); and if it doesn't, checks whether its parent class
(and then its grandparent, and so on) has a class attribute called
"f", and if it does, calls f(x, *args)?
-- 
http://mail.python.org/mailman/listinfo/python-list


Adding an interface to existing classes

2011-12-22 Thread Spencer Pearson
I'm writing a geometry package, with Points and Lines and Circles and
so on, and eventually I want to be able to draw these things on the
screen. I have two options so far for how to accomplish this, but
neither of them sits quite right with me, and I'd like the opinion of
comp.lang.python's wizened elders.

Option 1. Subclassing.
The most Pythonic way would seem to be writing subclasses for the
things I want to display, adding a ".draw(...)" method to each one,
like this:
class DrawablePoint( geometry.Point ):
class draw( self, ... ):
...

When the time comes to draw things, I'll have some list of objects I
want drawn, and say
for x in to_draw:
x.draw(...)

I see a problem with this, though. The intersection of two lines is
(usually) an object of type Point. Since DrawableLine inherits from
Line, this means that unless I redefine the "intersect" method in
DrawableLine, the intersection of two DrawableLines will be a Point
object, not a DrawablePoint. I don't want to saddle the user with the
burden of converting every method output into its corresponding
Drawable subclass, and I don't want to redefine every method to return
an instance of said subclass. I see no other options if I continue
down the subclassing path. Am I missing something?




Option 2. A "draw" function, with a function dictionary.
This feels weird, but is fairly simple to write, use, and extend. We
have a module with a "draw_functions" dictionary that maps types onto
functions, and a "draw" function that just looks up the proper type in
the dictionary and calls the corresponding function. If you create
your own object, you can just add a new entry to the dictionary. The
implementation is simple enough to outline here:

In file "geometry/gui.py":
def draw_point(...):
...
def draw_line(...):
...
draw_functions = {geometry.Point: draw_point, geometry.Line:
draw_line, ...}
def draw( x, *args, **kwargs ):
for type, callback in draw_functions.iteritems():
if isinstance(x, type):
callback(x, *args, **kwargs)
else:
raise TypeError("don't know how to draw things of type "
"{0}".format(type(x)))


In the file that uses this:
# Drawing a predefined type of object:
geometry.gui.draw(some_point, ...)
# Here we define a new kind of object and tell the package how to draw
it.
class MyObject(GeometricObject):
...
def draw_my_object(...):
...
geometry.gui.draw_functions[MyObject] = draw_my_object
# And now we draw it.
geometry.gui.draw(MyObject(...), ...)


If I feel fancy, I might use a decorator for adding entries to
draw_functions, but this is basically how it'd work.




The second way feels kludgey to me, but I'm leaning towards it because
it seems like so much less work and I'm out of ideas. Can anyone help,
explaining either a different way to do it or why one of these isn't
as bad as I think?

Thanks for your time!
-Spencer
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Adding an interface to existing classes

2011-12-24 Thread Spencer Pearson
On Dec 23, 9:13 am, Terry Reedy  wrote:
> On 12/22/2011 3:21 AM, Spencer Pearson wrote:
>
> > I'm writing a geometry package, with Points and Lines and Circles and
> > so on, and eventually I want to be able to draw these things on the
> > screen. I have two options so far for how to accomplish this, but
> > neither of them sits quite right with me, and I'd like the opinion of
> > comp.lang.python's wizened elders.
>
> > Option 1. Subclassing.
> > The most Pythonic way would seem to be writing subclasses for the
> > things I want to display, adding a ".draw(...)" method to each one,
> > like this:
> > Option 2. A "draw" function, with a function dictionary.
>
> Option 3? Add a draw method to existing classes, rather than subclassing?
>
> --
> Terry Jan Reedy

Thanks for the response! Do you mean something like this?
class Point(GeometricObject):
def intersect(self, other):
...
def union(self, other):
...
def draw(self, ...):
...

I'd like to avoid this, because... well, what I want is a geometry
package, and it seems to me that whistles and bells like GUI support
ought to be confined to subpackages. I'd look at this and think, "the
rest of the Point class deals with fundamental geometric reality.
What's this GUI method doing mixed in with my beautiful mathematical
purity?" Is this the wrong attitude to take?

Or did you mean this?
In file "geometry/gui.py":
def draw_point(point, ...):
...
Point.draw = draw_point

I've never modified an existing class before, and I fear the
unfamiliar. If that's what you meant... it's really an acceptable
thing to do? It seems like somebody might see "some_point.draw(...)"
and be confused by the method's absence in the Point class definition.

(A hybrid approach would be to say "draw = geometry.gui.draw_point"
inside the body of the Point class, setting the attribute there
instead of in gui.py, but that makes me uneasy for the same reason as
putting the function's full definition in the class.)

Thanks again,
-Spencer
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Adding an interface to existing classes

2012-01-05 Thread Spencer Pearson
(I'm sorry for my delayed response -- I've been travelling and not had
reliable Internet access.)

>> Spencer, i would re-think this entire project from the
>> beginning. You are trying to make an object out of everything. You
>> don't need to make an object of EVERYTHING.
>
> Very true.

I'm not sure I understand. Surely you're not both saying that I
shouldn't write a Point class? Here's an expression I'd like very
much to be able to type:

sphere1.intersect(sphere2).rotated(angle, axis)

If I represented points with something already defined, maybe a tuple
or a numpy array, then that would risk that the spheres were tangent,
that their intersection was not a Circle (a GeometricObject, with a
.rotated method), but a point (a tuple, without one).

On 2011-12-25, Steven D'Aprano 
wrote:
> class Line:
> intersection_kind = Point
> def intersection(self, other):
> blah()
> return self.intersection_kind(a, b)
>
> class DrawableLine(Line):
> intersection_kind = DrawablePoint

If I objected that the intersection of two Lines might, once in a blue
moon, be a Line (because they're the same line), this seems like it
would edge towards the factory solution that Ian Kelly suggested
(i.e. not "return self.intersection_kind(...)", but "return
self.factory.create_point(...)" or something similar). Is that so?

-Spencer
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Adding an interface to existing classes

2012-01-05 Thread Spencer Pearson
On Dec 25 2011, 2:58 pm, Terry Reedy  wrote:
> On 12/24/2011 6:49 PM,SpencerPearsonwrote:
>
> > On Dec 23, 9:13 am, Terry Reedy  wrote:
> >> On 12/22/2011 3:21 AM,SpencerPearsonwrote:
>
> >>> I'm writing a geometry package, with Points and Lines and Circles and
> >>> so on, and eventually I want to be able to draw these things on the
> >>> screen. I have two options so far for how to accomplish this, but
> >>> neither of them sits quite right with me, and I'd like the opinion of
> >>> comp.lang.python's wizened elders.
>
> >>> Option 1. Subclassing.
> >>> The most Pythonic way would seem to be writing subclasses for the
> >>> things I want to display, adding a ".draw(...)" method to each one,
> >>> like this:
>
> There are people who would advocate a Drawable base class with a virtual
> or abstract .draw method and that DrawablePoint, etc, inherit from
> Drawable and Point.
>
> >>> Option 2. A "draw" function, with a function dictionary.
>
> >> Option 3? Add a draw method to existing classes, rather than subclassing?
> > Thanks for the response! Do you mean something like this?
> > class Point(GeometricObject):
> >      def intersect(self, other):
> >          ...
>
> I am interpreting this to mean that you have a world coordinate system
> for instances that have location and size.
>
> >      def union(self, other):
> >          ...
> >      def draw(self, ...):
> >          ...
>
> Yes. I would consider that Option 0, the default, unless you have good
> reason to choose another. I would certainly include it on a list of options.
>
> > I'd like to avoid this, because... well, what I want is a geometry
> > package, and it seems to me that whistles and bells like GUI support
> > ought to be confined to subpackages. I'd look at this and think, "the
> > rest of the Point class deals with fundamental geometric reality.
> > What's this GUI method doing mixed in with my beautiful mathematical
> > purity?"
>
> By default, all Python objects have a text representation method. I do
> not see that giving all concrete geometric objects (with a location and
> size) a visual representation is much different. I would use drawing
> functions that accept the coordinates and distances of your geometry
> world and translate to low-level pixel functions for a particular gui
> system. I agree that your geometrical objects should not know about
> pixels, screens, windows, and aspect ratios.
>
>  > Is this the wrong attitude to take?
>
> It depends on *your* goal and values.
>
> > Or did you mean this?
> > In file "geometry/gui.py":
> > def draw_point(point, ...):
> >      ...
> > Point.draw = draw_point
>
> > I've never modified an existing class before, and I fear the
> > unfamiliar. If that's what you meant... it's really an acceptable
> > thing to do?
>
> Yes, in my opinion. The advantage of this is putting all the draw
> methods together, and possibly having more than one one set. On the
> other hand, one needs to know the data attributes of each class to
> understand its draw method.
>
> > It seems like somebody might see "some_point.draw(...)"
> > and be confused by the method's absence in the Point class definition.
>
> With either suboption, you should put an abstract .draw method in the
> GeometricObject base class.
>
> I would look at various game, graph, geometry, and gui packages handle
> drawing for more ideas.
>
> --
> Terry Jan Reedy

(I'm sorry for my delayed response -- I've been travelling and not had
reliable Internet access.)

On 2011-12-25, Terry Reedy  wrote:
> There are people who would advocate a Drawable base class with a virtual
> or abstract .draw method and that DrawablePoint, etc, inherit from
> Drawable and Point.

Yes... yes, that makes sense to me.

> By default, all Python objects have a text representation method. I do
> not see that giving all concrete geometric objects (with a location and
> size) a visual representation is much different. I would use drawing
> functions that accept the coordinates and distances of your geometry
> world and translate to low-level pixel functions for a particular gui
> system. I agree that your geometrical objects should not know about
> pixels, screens, windows, and aspect ratios.

Ha! Oh, I've been being silly. I was going to claim that since there
is no standard Python GUI, I ought not chain myself to any one of the
candidates. Then I learned that Tkinter comes standard with Python.
Oops.

All right, now that I know that, the comparison to having a text
representation seems very reasonable. I'll definitely reconsider
making the draw() method a requirement for all GeometricObjects.

>> I've never modified an existing class before, and I fear the
>> unfamiliar. If that's what you meant... it's really an acceptable
>> thing to do?
>
> Yes, in my opinion. The advantage of this is putting all the draw
> methods together, and possibly having more than one one set. On the
> other hand, one needs to know the data attributes of each class to
> understand its draw me

Re: Adding an interface to existing classes

2012-01-05 Thread Spencer Pearson
(I'm sorry for my delayed response -- I've been travelling and not had
reliable Internet access.)

On 2011-12-25, Ian Kelly  wrote:
> On Thu, Dec 22, 2011 at 1:21 AM, Spencer Pearson
> wrote:
>> I see a problem with this, though. The intersection of two lines is
>> (usually) an object of type Point. Since DrawableLine inherits from
>> Line, this means that unless I redefine the "intersect" method in
>> DrawableLine, the intersection of two DrawableLines will be a Point
>> object, not a DrawablePoint. I don't want to saddle the user with the
>> burden of converting every method output into its corresponding
>> Drawable subclass, and I don't want to redefine every method to return
>> an instance of said subclass. I see no other options if I continue
>> down the subclassing path. Am I missing something?
>
> You could solve this with a factory. Instead of having
> Line.intersection() create a Point directly, have it call
> self.factory.create_point().  In the drawing module, replace the
> factory for the subclasses with one that creates drawable classes
> instead.

Oh, that's a neat idea. Yes, I think that does exactly what I want!
Thanks very much!

> This will also work, but inheritance complicates things a
> little. ...
> ... Probably the easiest way to do this correctly is to follow the
> MRO that Python has conveniently already built for you.  Something
> like:
>
> def draw(x, *args, **kwargs ):
> for class_ in type(x).__mro__:
> if class_ in draw_functions:
> draw_functions[class_](*args, **kwargs)
> break
> else:
> raise TypeError("don't know how to draw things of type "
> "{0}".format(type(x)))

You're right, you're right. My implementation was sloppy. I think I'll
go with your factory solution, but thanks for the fixed version of
draw() -- I've never seen __mro__ before, and it seems to be just the
tool for this job!

-Spencer
-- 
http://mail.python.org/mailman/listinfo/python-list


Tuples vs. variable-length argument lists

2010-03-19 Thread Spencer Pearson
Hi!

This might be more of a personal-preference question than anything,
but here goes: when is it appropriate for a function to take a list or
tuple as input, and when should it allow a varying number of
arguments? It seems as though the two are always interchangeable. For
a simple example...

def subtract( x, nums ):
  return x - sum( nums )

... works equally well if you define it as "subtract( x, *nums )" and
put an asterisk in front of any lists/tuples you pass it. I can't
think of any situation where you couldn't convert from one form to the
other with just a star or a pair of parentheses.

Is there a generally accepted convention for which method to use? Is
there ever actually a big difference between the two that I'm not
seeing?
-- 
http://mail.python.org/mailman/listinfo/python-list


Class changes in circular imports when __name__ == '__main__'

2010-09-05 Thread Spencer Pearson
Hi! I'm writing a package with several files in it, and I've found
that "isinstance" doesn't work the way I expect under certain
circumstances.

Short example: here are two files.
# fileone.py
import filetwo

class AClass( object ):
  pass

if __name__ == '__main__':
  a = AClass()
  filetwo.is_aclass( a )

# filetwo.py

import fileone

def is_aclass( a ):
  print "The argument is", ("" if isinstance(a, fileone.AClass) else
"not"), "an instance of fileone.AClass"


If you run fileone.py, it will tell you that "The argument is not an
instance of fileone.AClass", which seems strange to me, given that the
fileone module is the one that CREATES the object with its own AClass
class. And if you replace "if __name__ == '__main__'" with "def
main()", start Python, import fileone, and call fileone.main(), it
tells you that the argument IS an instance of AClass.

So, the module's name change to __main__ when you run it on its own...
well, it looks like it puts all of the things defined in fileone in
the __main__ namespace INSTEAD of in the fileone module's namespace,
and then when filetwo imports fileone, the class is created again,
this time as fileone.AClass, and though it's identical in function to
__main__.AClass, one "is not" the other.

Is this kind of doubled-back 'isinstance' inherently sinful? I mean, I
could solve this problem by giving all of my classes "classname"
attributes or something, but maybe it's just a sign that I shouldn't
have to do this in the first place.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Class changes in circular imports when __name__ == '__main__'

2010-09-08 Thread Spencer Pearson
All right, thank you for helping! I'd had a little voice in the back
of my mind nagging me that it might not be logical to include a bunch
of classes and function definitions in my startup file, but I never
got around to splitting it up. The module/script distinction makes
sense, and it seems more elegant, too. Also, my program works now that
I've rearranged things, which is a plus. Thanks!
-- 
http://mail.python.org/mailman/listinfo/python-list