Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #17721 > unrolled thread

Adding an interface to existing classes

Started bySpencer Pearson <speeze.pearson@gmail.com>
First post2011-12-22 00:21 -0800
Last post2012-01-04 23:56 -0800
Articles 15 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  Adding an interface to existing classes Spencer Pearson <speeze.pearson@gmail.com> - 2011-12-22 00:21 -0800
    Re: Adding an interface to existing classes Terry Reedy <tjreedy@udel.edu> - 2011-12-22 18:13 -0500
      Re: Adding an interface to existing classes Spencer Pearson <speeze.pearson@gmail.com> - 2011-12-24 15:49 -0800
        Re: Adding an interface to existing classes Terry Reedy <tjreedy@udel.edu> - 2011-12-24 20:58 -0500
          Re: Adding an interface to existing classes Spencer Pearson <speeze.pearson@gmail.com> - 2012-01-04 23:55 -0800
    Re: Adding an interface to existing classes Ian Kelly <ian.g.kelly@gmail.com> - 2011-12-24 18:16 -0700
      Re: Adding an interface to existing classes Spencer Pearson <speeze.pearson@gmail.com> - 2012-01-04 23:54 -0800
    Re: Adding an interface to existing classes Rick Johnson <rantingrickjohnson@gmail.com> - 2011-12-24 17:24 -0800
      Re: Adding an interface to existing classes Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-25 12:10 +0000
        Re: Adding an interface to existing classes Chris Angelico <rosuav@gmail.com> - 2011-12-25 23:32 +1100
          Re: Adding an interface to existing classes Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-25 13:27 +0000
            Re: Adding an interface to existing classes Chris Angelico <rosuav@gmail.com> - 2011-12-26 00:37 +1100
              Re: Adding an interface to existing classes Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-25 13:46 +0000
                Re: Adding an interface to existing classes Chris Angelico <rosuav@gmail.com> - 2011-12-26 00:55 +1100
                Re: Adding an interface to existing classes Spencer Pearson <speeze.pearson@gmail.com> - 2012-01-04 23:56 -0800

#17721 — Adding an interface to existing classes

FromSpencer Pearson <speeze.pearson@gmail.com>
Date2011-12-22 00:21 -0800
SubjectAdding an interface to existing classes
Message-ID<8f345777-1ef7-46dc-b3fb-a0bea5ebf2c3@r16g2000prr.googlegroups.com>
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

[toc] | [next] | [standalone]


#17757

FromTerry Reedy <tjreedy@udel.edu>
Date2011-12-22 18:13 -0500
Message-ID<mailman.4002.1324595653.27778.python-list@python.org>
In reply to#17721
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

[toc] | [prev] | [next] | [standalone]


#17868

FromSpencer Pearson <speeze.pearson@gmail.com>
Date2011-12-24 15:49 -0800
Message-ID<256b9bcf-aa08-4d3e-9928-8fa9d335f1ff@e8g2000prb.googlegroups.com>
In reply to#17757
On Dec 23, 9:13 am, Terry Reedy <tjre...@udel.edu> 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

[toc] | [prev] | [next] | [standalone]


#17874

FromTerry Reedy <tjreedy@udel.edu>
Date2011-12-24 20:58 -0500
Message-ID<mailman.4058.1324778341.27778.python-list@python.org>
In reply to#17868
On 12/24/2011 6:49 PM, Spencer Pearson wrote:
> On Dec 23, 9:13 am, Terry Reedy<tjre...@udel.edu>  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:

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

[toc] | [prev] | [next] | [standalone]


#18537

FromSpencer Pearson <speeze.pearson@gmail.com>
Date2012-01-04 23:55 -0800
Message-ID<93f85e9e-ddb2-47c0-9e58-8234c7efc825@q11g2000vbq.googlegroups.com>
In reply to#17874
On Dec 25 2011, 2:58 pm, Terry Reedy <tjre...@udel.edu> wrote:
> On 12/24/2011 6:49 PM,SpencerPearsonwrote:
>
> > On Dec 23, 9:13 am, Terry Reedy<tjre...@udel.edu>  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 <tjreedy@udel.edu> 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 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.

Sure, sure. I'm comfortable with this way of doing things now. Thanks
so much for all your help!

-Spencer

[toc] | [prev] | [next] | [standalone]


#17872

FromIan Kelly <ian.g.kelly@gmail.com>
Date2011-12-24 18:16 -0700
Message-ID<mailman.4057.1324775834.27778.python-list@python.org>
In reply to#17721
On Thu, Dec 22, 2011 at 1:21 AM, Spencer Pearson
<speeze.pearson@gmail.com> 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.


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

This will also work, but inheritance complicates things a little.  Do
you do a type equality check or an isinstance() check when looking up
the type in the dictionary?  If the former, then somebody who
subclasses geometry.Line to create a GroovyLine subclass must add an
entry for GroovyLine to the dictionary, even if the drawing code is
the same.  The drawing code is not inherited.

But if you do an isinstance() check, then you need to be very careful
about how you check it.  If somebody creates a WavyLine subclass and
registers a different drawing method, then you need to make sure the
isinstance() check for the WavyLine implementation happens before the
regular Line implementation, or the wrong drawing code may get invoked
for WavyLine.  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)))

Cheers,
Ian

[toc] | [prev] | [next] | [standalone]


#18539

FromSpencer Pearson <speeze.pearson@gmail.com>
Date2012-01-04 23:54 -0800
Message-ID<ec5f760f-2145-471a-a6e0-2b53da83d891@a11g2000vbz.googlegroups.com>
In reply to#17872
(I'm sorry for my delayed response -- I've been travelling and not had
reliable Internet access.)

On 2011-12-25, Ian Kelly <ian.g.kelly@gmail.com> wrote:
> On Thu, Dec 22, 2011 at 1:21 AM, Spencer Pearson
><speeze.pearson@gmail.com> 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

[toc] | [prev] | [next] | [standalone]


#17873

FromRick Johnson <rantingrickjohnson@gmail.com>
Date2011-12-24 17:24 -0800
Message-ID<22ea371a-f282-4d81-89f6-bc3229e2e8b1@a17g2000yqj.googlegroups.com>
In reply to#17721
On Dec 22, 2:21 am, Spencer Pearson <speeze.pear...@gmail.com> 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.

...which is the main reason for creating a geometry package in the
first place!. I mean, imaginary points and lines are great if you are
a theoretical mathematician, but for the rest of us earthlings,
plotted pixels are the "in thang" now-a-days.

> 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, ... ):
>         ...

"DrawablePoint"? I suppose there is an "ImaginaryPoint" somewhere in
this API? Geez

> 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(...)

Seems reasonable. Not the fastest approach, but a good start for a
very high level interface.

> I see a problem with this, though. The intersection of two lines is
> (usually) an object of type Point. Since DrawableLine inherits from
> Line,

Why the hell is "Drawable" inheriting from Line? I would think that a
"Line" would be "Drawable" object and NOT vice-versa? Am i wrong?

> this means that unless I redefine the "intersect" method in
> DrawableLine, the intersection of two DrawableLines will be a Point
> object, not a DrawablePoint.

OMFG!

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

Yes, a proper object hierarchy and API it seems @_@.

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. Ask yourself, what are the most basic objects
of a geometry library, and then report back to us your findings.

PS: I prefer option1 for these things as the OOP paradigm fits nicely.
I just hate to have modules of loose functions just lying about.

[toc] | [prev] | [next] | [standalone]


#17882

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-25 12:10 +0000
Message-ID<4ef712c0$0$29973$c3e8da3$5496439d@news.astraweb.com>
In reply to#17873
On Sat, 24 Dec 2011 17:24:14 -0800, Rick Johnson wrote:

>> class DrawablePoint( geometry.Point ):
>>     class draw( self, ... ):
>>         ...
> 
> "DrawablePoint"? I suppose there is an "ImaginaryPoint" somewhere in
> this API? Geez

No, but there's an abstract Point, which presumably refers to the 
geometric concept (hence the module, geometry) without concerning itself 
with such things as pixels, raster and vector output devices, screen 
resolutions, and all the other stuff which is needed for drawing points 
but not needed for working with points.


[...]
>> I see a problem with this, though. The intersection of two lines is
>> (usually) an object of type Point. Since DrawableLine inherits from
>> Line,
> 
> Why the hell is "Drawable" inheriting from Line? I would think that a
> "Line" would be "Drawable" object and NOT vice-versa? Am i wrong?

Probably. I think there's a case for Drawable to be an abstract mixin 
class, so that DrawableLine inherits from both Line and Drawable.


>> this means that unless I redefine the "intersect" method in
>> DrawableLine, the intersection of two DrawableLines will be a Point
>> object, not a DrawablePoint.

Not if you define intersect in Point correctly in the first place.

class Point:  # An abstract class.
    def intersect(self, other):
        blah; blah; blah
        return Point(x, y)  # No, wrong, bad!!! Don't do this.

Instead:

        return self.__class__(x, y)  # Better.


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



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#17883

FromChris Angelico <rosuav@gmail.com>
Date2011-12-25 23:32 +1100
Message-ID<mailman.4063.1324816364.27778.python-list@python.org>
In reply to#17882
On Sun, Dec 25, 2011 at 11:10 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> class Point:  # An abstract class.
>    def intersect(self, other):
>        blah; blah; blah
>        return Point(x, y)  # No, wrong, bad!!! Don't do this.
>
> Instead:
>
>        return self.__class__(x, y)  # Better.

This would work if you were dealing with the intersection of two
points, but how do you use that sort of trick for different classes?

ChrisA

[toc] | [prev] | [next] | [standalone]


#17891

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-25 13:27 +0000
Message-ID<4ef724d4$0$29973$c3e8da3$5496439d@news.astraweb.com>
In reply to#17883
On Sun, 25 Dec 2011 23:32:41 +1100, Chris Angelico wrote:

> On Sun, Dec 25, 2011 at 11:10 PM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> class Point:  # An abstract class.
>>    def intersect(self, other):
>>        blah; blah; blah
>>        return Point(x, y)  # No, wrong, bad!!! Don't do this.
>>
>> Instead:
>>
>>        return self.__class__(x, y)  # Better.
> 
> This would work if you were dealing with the intersection of two points,
> but how do you use that sort of trick for different classes?

There's nothing in the above that assumes that other has the same type as 
self. It's just that the type of other is ignored, and the type of self 
always wins. I find that a nice, clear rule: x.intersection(y) always 
returns a point with the same type as x.

If you want a more complicated rule, you have to code it yourself:


def intersection(self, other):
    if issubclass(type(other), type(self)):
        kind = type(other)
    elif issubclass(type(self), type(other)):
        kind = AbstractPoint
    elif other.__class__ is UserPoint:
        kind = UserPoint
    elif today is Tuesday:
        kind = BelgiumPoint
    else:
        kind = self.__class__
    return kind(x, y)


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#17894

FromChris Angelico <rosuav@gmail.com>
Date2011-12-26 00:37 +1100
Message-ID<mailman.4067.1324820245.27778.python-list@python.org>
In reply to#17891
On Mon, Dec 26, 2011 at 12:27 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> There's nothing in the above that assumes that other has the same type as
> self. It's just that the type of other is ignored, and the type of self
> always wins. I find that a nice, clear rule: x.intersection(y) always
> returns a point with the same type as x.

The intersection of DrawableLine and DrawableLine is DrawablePoint.
That's not the same type as either of the inputs. Same if you seek the
intersection of two planes, which is a line - or two spheres, which is
a circle (with possible failure if they don't intersect).

ChrisA

[toc] | [prev] | [next] | [standalone]


#17895

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-25 13:46 +0000
Message-ID<4ef72927$0$29973$c3e8da3$5496439d@news.astraweb.com>
In reply to#17894
On Mon, 26 Dec 2011 00:37:22 +1100, Chris Angelico wrote:

> On Mon, Dec 26, 2011 at 12:27 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> There's nothing in the above that assumes that other has the same type
>> as self. It's just that the type of other is ignored, and the type of
>> self always wins. I find that a nice, clear rule: x.intersection(y)
>> always returns a point with the same type as x.
> 
> The intersection of DrawableLine and DrawableLine is DrawablePoint.
> That's not the same type as either of the inputs. Same if you seek the
> intersection of two planes, which is a line - or two spheres, which is a
> circle (with possible failure if they don't intersect).

class Line:
    intersection_kind = Point
    def intersection(self, other):
        blah()
        return self.intersection_kind(a, b)

class DrawableLine(Line):
    intersection_kind = DrawablePoint




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#17901

FromChris Angelico <rosuav@gmail.com>
Date2011-12-26 00:55 +1100
Message-ID<mailman.4071.1324821326.27778.python-list@python.org>
In reply to#17895
On Mon, Dec 26, 2011 at 12:46 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> class DrawableLine(Line):
>    intersection_kind = DrawablePoint

Ha! That works. I was sure there'd be some simple way to do it!

ChrisA

[toc] | [prev] | [next] | [standalone]


#18536

FromSpencer Pearson <speeze.pearson@gmail.com>
Date2012-01-04 23:56 -0800
Message-ID<d88421e9-1325-402a-9af9-aa76f7173c94@w4g2000vbc.googlegroups.com>
In reply to#17895
(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 <steve+comp.lang.python@pearwood.info>
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

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web