cool python tricks


Man I love Python. I came up with a neat trick yesterday that also couldn’t be done in any static language. Needless to say, I’m pretty pleased with myself. This trick isn’t slow or hard to understand, and actually makes a lot of my code very simple, and avoids a lot of boilerplate that I would have had to write in another language

I needed a way to given a basic color to a class, and then have easy access to various tints of that color for painting different aspects of an object. The tints are based on HSV, not RGB, but all the callers need to deal with RGB.

The solution: wrap the property() descriptor with my own descriptor.

I start by defining my class

class TintedColors(object):
    def __init__(self, color):
        """
        color is an RGB triple
        """
        self.color = color
        self.hue = rgb_to_hsv(*color)[0]

Now I just need to define the property wrapper:

    def tintedColor(saturation, value=1.0):
        def getColor(self):
            hsv = (self.hue, saturation, value)
            return hsv_to_rgb(*hsv)
        return property(getColor)

Finally, I can define all my tints as class attributes:

    gradientLeft = tintedColor(0.4)
    gradientRight = tintedColor(0.2)
    outlineColor = tintedColor(0.5)
    textColor = tintedColor(0.67, 0.6)

What is so amazing about this is that because property is a runtime thing, and because classes are evaluated, not declared, the actual value of “f.gradientLeft” will be a descriptor as generated by property(). Each descriptor will call a special version of getColor() defined for each and every tint. The dynamic nature of this might make it look like it should be slower than a simple property() call, but in fact it is just as fast because the specialized versions of getColor() are very simple, and in memory act as though they were declared as:

def getColor(self):
    hsv = (self.hue, 0.4, 1.0)
    return hsv_to_rgb(*hsv)

which is pretty darn fast.

Lets look at a similar option in C++:

class TintedColors {
private:
    color mColor;
    float mHue;
 
    color GetSaturatedColor(float saturation, float value=1.0) {
        hsv = make_hsv(this.mHue, saturation, value);
        return hsv_to_rgb(hsv);
    }
 

public:
    TintedColors(const color& c) const {
        mColor = c;
        this.mHue = rgb_to_hsv(c).hue
    }
 

    color& GetGradientLeft() const {
        return GetSaturatedColor(0.4);
    }
 
    color& GetOutlineColor() const {
        return GetSaturatedColor(0.2);
    }
 
    color& GetTextColor() const {
        return GetSaturatedColor(0.67, 0.6);
    }
}

There we go. I’ve tried to follow “good C++” guidelines with respect to consts, references, and so forth. And there we have the same pattern in C++ but with way more work: the 4 boilerplate calls meant I had to type GetSaturatedColor() 4 times, and the verbosity allows you to loose what’s really different between these functions – the color saturation and value.

And that’s all assuming that I’ve got a “color” type (which I’d have to define seperately – python just uses the built-in triples for a small value like that) and that hsv_to_rgb is part of that color system.. that may seem like a small assumption. It may not be part of the language, but there are so many extra easy routines just built in to Python that you can’t just write that off.

  1. No comments yet.
(will not be published)