ANN: ThinWrap and ReMap

Hello,

I have been working on a couple of interesting concoctions for matplotlib. The first is a wrapper class called “ThinWrap” that, essentially, provides a way to create objects that are linked to a given object. These objects can then be subclassed for some very interesting behaviors. Which leads me to my ReMap class.

The ReMap class is designed to be a wrapper around a Colormap object, and the call function is overloaded so that some other defined function can modify the rgba values that comes from a call to the original colormap object. All of this is done without modifying the original colormap. In addition, a ReMap object can wrap another ReMap object, allowing for stacking. As an example, I have created a ToGrayscale() ReMap class.

For your reviewing pleasure, there are two patch files. The first, thinwrap.patch, adds the ThinWrap class to cbook.py. The second, remap.patch, creates a new file “remap.py” that holds the ReMap class and the ToGrayscale class. In addition, in order for things to work, a few changes had to be made to other code:

cm.py: get_cmap() could finish the function without returning anything. I modified it to remove the “isinstance” check that would cause non-Colormap/non-string objects to fall into a black hole. We are gonna have to follow duck-typing here…

colors.py: The Colormap class needs to be a new-style class for ReMap to work properly
contour.py: Commented out code that did a isinstance() check on the cmap that would cause ReMaps on contours to fail.

I have also included an example script to demonstrate how this wrapper class works and the ToGrayscale() class works.

Let me know what you think!
Ben Root

thinwrap.patch (4.24 KB)

remap.patch (4.92 KB)

testremap.py (752 Bytes)

Just reping-ing this. I haven’t heard anything negative and got a few positive comments off-list. I haven’t committed this yet because I am concerned about the implications of my changes to cm.py, colors.py and contour.py. However, if I don’t hear any concerns over the next couple of days, shall I assume that it is ok to go ahead and commit?

Ben Root

···

On Tue, Aug 31, 2010 at 9:08 PM, Benjamin Root <ben.root@…854…> wrote:

Hello,

I have been working on a couple of interesting concoctions for matplotlib. The first is a wrapper class called “ThinWrap” that, essentially, provides a way to create objects that are linked to a given object. These objects can then be subclassed for some very interesting behaviors. Which leads me to my ReMap class.

The ReMap class is designed to be a wrapper around a Colormap object, and the call function is overloaded so that some other defined function can modify the rgba values that comes from a call to the original colormap object. All of this is done without modifying the original colormap. In addition, a ReMap object can wrap another ReMap object, allowing for stacking. As an example, I have created a ToGrayscale() ReMap class.

For your reviewing pleasure, there are two patch files. The first, thinwrap.patch, adds the ThinWrap class to cbook.py. The second, remap.patch, creates a new file “remap.py” that holds the ReMap class and the ToGrayscale class. In addition, in order for things to work, a few changes had to be made to other code:

cm.py: get_cmap() could finish the function without returning anything. I modified it to remove the “isinstance” check that would cause non-Colormap/non-string objects to fall into a black hole. We are gonna have to follow duck-typing here…

colors.py: The Colormap class needs to be a new-style class for ReMap to work properly
contour.py: Commented out code that did a isinstance() check on the cmap that would cause ReMaps on contours to fail.

I have also included an example script to demonstrate how this wrapper class works and the ToGrayscale() class works.

Let me know what you think!
Ben Root

2010/9/13 Benjamin Root <ben.root@...553...>:

I have been working on a couple of interesting concoctions for
matplotlib. The first is a wrapper class called "ThinWrap" that,
essentially, provides a way to create objects that are linked to a given
object. These objects can then be subclassed for some very interesting
behaviors. Which leads me to my ReMap class.

The ReMap class is designed to be a wrapper around a Colormap object, and
the __call__ function is overloaded so that some other defined function can
modify the rgba values that comes from a call to the original colormap
object. All of this is done without modifying the original colormap. In
addition, a ReMap object can wrap another ReMap object, allowing for
stacking. As an example, I have created a ToGrayscale() ReMap class.

I must admit that I have serious trouble accepting the need for this
idea. To me, it seems like a not very elegant exploitation of Python
features in a way they aren't intended to be used.

Generally, I think, implementations shouldn't rely on the internals of
the implementation of object orientation, in a case like this, when
they try to expand the features of the object orientation the language
supplies.

I think you cannot speak of "subclassing" when creating a ThinWrap
object around another object? (From your patch, it looks to me like
this.)

Furthermore, I'd say, this ThinWrap feature is neither specially
needed for matplotlib's usecase, nor is it widely accepted. It is
like an independent package incorporated into matplotlib the hard way.
I think this isn't the way is should be. In that case, it may be a
dependency, which should be handled by easy_install or the user.

Benjamin, I believe this code is neat in idea, but not suited well for
Python. I would refrain from checking it in.

Friedrich

P.S.: It seems to me like a forced generalisation of your original
idea of a wrap around the Colormap object. If I would be you, I'd
restrict the usecase to Colormap, and implement a real class, which is
just a WrappedColormap, derived from Colormap, which can do the job by
overloading.

Right from the need that you have to circumvent normal isinstance()
checks, which shouldn't be an issue in a clear oo implementation of
your functionality, you can see that your approach is not well suited
in my opinion.

Don't be offended, I have to add.

I think you run into serious problems when trying to find a place for
the class of ThinWrap in the oo model.

Can you ask the authors of the pro posts to re-post their posts on the
list? I'd be interested.

···

On Tue, Aug 31, 2010 at 9:08 PM, Benjamin Root <ben.root@...553...> wrote:

cm.py: get_cmap() could finish the function without returning anything. I
modified it to remove the "isinstance" check that would cause
non-Colormap/non-string objects to fall into a black hole. We are gonna
have to follow duck-typing here...
colors.py: The Colormap class needs to be a new-style class for ReMap to
work properly
contour.py: Commented out code that did a isinstance() check on the cmap
that would cause ReMaps on contours to fail.

I have also included an example script to demonstrate how this wrapper
class works and the ToGrayscale() class works.

Let me know what you think!
Ben Root

Just reping-ing this. I haven't heard anything negative and got a few
positive comments off-list. I haven't committed this yet because I am
concerned about the implications of my changes to cm.py, colors.py and
contour.py. However, if I don't hear any concerns over the next couple of
days, shall I assume that it is ok to go ahead and commit?

Ben Root

I haven't looked closely at this yet, but at a glance it feels like a
heavy-weight solution to what should be a simple problem. Can we not
just have a "to_grayscale method of the colormap? Also, I second
Friedrich's concern/question about the ThinWrap approach -- what is
the argument for wrapping vs deriving?

JDH

···

On Mon, Sep 13, 2010 at 4:27 PM, Benjamin Root <ben.root@...553...> wrote:

Just reping-ing this. I haven't heard anything negative and got a few
positive comments off-list. I haven't committed this yet because I am
concerned about the implications of my changes to cm.py, colors.py and
contour.py. However, if I don't hear any concerns over the next couple of
days, shall I assume that it is ok to go ahead and commit?

2010/9/13 Benjamin Root <ben.root@…553…>:

I have been working on a couple of interesting concoctions for

matplotlib. The first is a wrapper class called “ThinWrap” that,

essentially, provides a way to create objects that are linked to a given

object. These objects can then be subclassed for some very interesting

behaviors. Which leads me to my ReMap class.

The ReMap class is designed to be a wrapper around a Colormap object, and

the call function is overloaded so that some other defined function can

modify the rgba values that comes from a call to the original colormap

object. All of this is done without modifying the original colormap. In

addition, a ReMap object can wrap another ReMap object, allowing for

stacking. As an example, I have created a ToGrayscale() ReMap class.

I must admit that I have serious trouble accepting the need for this

idea. To me, it seems like a not very elegant exploitation of Python

features in a way they aren’t intended to be used.

I agree. I never quite felt “right” about ThinWrap, which is part of my hesitation to include it.

Generally, I think, implementations shouldn’t rely on the internals of

the implementation of object orientation, in a case like this, when

they try to expand the features of the object orientation the language

supplies.

True, this would paint us into a corner if, in the future, python changes how it implements the object orientation internals in such a way that the desired behaviors aren’t possible.

I think you cannot speak of “subclassing” when creating a ThinWrap

object around another object? (From your patch, it looks to me like

this.)

I wasn’t exactly sure what is the proper word to use here. You are right, subclassing isn’t quite correct. It really is more like a faked inheritance (the ThinWrap object ‘inherits’ most of the methods and attributes of the object it wraps).

Furthermore, I’d say, this ThinWrap feature is neither specially

needed for matplotlib’s usecase, nor is it widely accepted. It is

like an independent package incorporated into matplotlib the hard way.

I think this isn’t the way is should be. In that case, it may be a

dependency, which should be handled by easy_install or the user.

I don’t know if I need to go that far. The code itself is really tiny and hardly worth being a package unto itself. Also, it would be hard to place it into the context of the rest of the python ecosystem. This is partly the reason why I thought placing it in cbook would be a good balance.

Benjamin, I believe this code is neat in idea, but not suited well for

Python. I would refrain from checking it in.

Friedrich

P.S.: It seems to me like a forced generalisation of your original

idea of a wrap around the Colormap object. If I would be you, I’d

restrict the usecase to Colormap, and implement a real class, which is

just a WrappedColormap, derived from Colormap, which can do the job by

overloading.

That’s part of the problem, the job really couldn’t be done by subclassing. The objective is to be able to convert any Colormap into a Grayscale, so, either I’d have to overload each kind of Colormap, and have the user somehow reconstruct their Colormap object, (which isn’t trivial if they are using one of the premade colormaps), or create a function that produces a Colormap from an arbitrary Colormap (which assumes that all attributes have been made and that there are no further changes to the Norms or such later, and that later code does not need the subclassed features).

Essentially, we are looking at a transform problem rather than a data representation problem. We simply need to intercept the call to call and transform the output accordingly. Everything else should be ignored by ThinWrap and allowed to proceed as it usually would.

Right from the need that you have to circumvent normal isinstance()

checks, which shouldn’t be an issue in a clear oo implementation of

your functionality, you can see that your approach is not well suited

in my opinion.

I have been planning on looking to see if MetaClassing might solve my problem here. But, an argument can be made that we really shouldn’t be using isinstance() in the first place because python is designed around the idea of duck-typing.

Don’t be offended, I have to add.
I think you run into serious problems when trying to find a place for

the class of ThinWrap in the oo model.

I’m not, I really needed someone to do a sanity check for me.

Ben Root

···

On Wed, Sep 15, 2010 at 10:33 AM, Friedrich Romstedt <friedrichromstedt@…149…> wrote:

On Tue, Aug 31, 2010 at 9:08 PM, Benjamin Root <ben.root@…553…> wrote:

But, what would the “to_grayscale” method produce? And what about other possible transformations? Should we add other functions as well to invert, brighten, darken, etc?

Maybe another approach would be to have the Colormap class have an attribute function called “transform” that would, by default, be just a function that returns the input rgba array. Then, one could assign different transformation functions that would be used in the call function of the colormap?

Maybe that idea is better?

Ben Root

···

On Wed, Sep 15, 2010 at 10:44 AM, John Hunter <jdh2358@…149…> wrote:

On Mon, Sep 13, 2010 at 4:27 PM, Benjamin Root <ben.root@…553…> wrote:

Just reping-ing this. I haven’t heard anything negative and got a few

positive comments off-list. I haven’t committed this yet because I am

concerned about the implications of my changes to cm.py, colors.py and

contour.py. However, if I don’t hear any concerns over the next couple of

days, shall I assume that it is ok to go ahead and commit?

I haven’t looked closely at this yet, but at a glance it feels like a

heavy-weight solution to what should be a simple problem. Can we not

just have a "to_grayscale method of the colormap? Also, I second

Friedrich’s concern/question about the ThinWrap approach – what is

the argument for wrapping vs deriving?

JDH

to_grayscale would return a colormap instance. If the colormap also
had a "darken" or "invert" (if people think this is useful), they
could also be colormap methods. Then you could chain

imshow(..., cmap=cm.jet.to_grayscale().darken())

KDJ

···

On Wed, Sep 15, 2010 at 11:31 AM, Benjamin Root <ben.root@...553...> wrote:

But, what would the "to_grayscale" method produce? And what about other
possible transformations? Should we add other functions as well to invert,
brighten, darken, etc?

Maybe another approach would be to have the Colormap class have an attribute
function called "transform" that would, by default, be just a function that
returns the input rgba array. Then, one could assign different
transformation functions that would be used in the __call__ function of the
colormap?