demangling ‘property’ values


I’m learning more about how properties work in Python. One thing I’m learning is that a property objects are only evaluated in the context of the parent object they’re attached to.

After my last property trick, I now needed a way to manage groups of color tints. After thinking about it for a while, I ended up with:

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

I thought, “I’m brilliant! I’m extending the dynamic nature of ‘property’ to defaultColors”

But unfortunately, I ended up with something ugly instead.

What I got back was:

>>> col.defaultColors
(<property object at 0x009D9530>, <property object at 0x01351530>, 
<property object at 0x013B5378>, <property object at 0x013B53A0>)

That’s not helpful at all! If I tried to access col.defaultColors[0], I got a property object rather than an rgb tuple.

I think the problem is that when I said col.defaultColors[0], the property object didn’t know it was being accessed as a property of an object, so it didn’t unwrap itself. It looked like I was going to have to do that unwrapping myself.

Sure enough, if I look at the property object, I can see what to do:

>>> dir(col.selectedColors[0])
['__class__', '__delattr__', '__delete__', '__doc__', '__get__', 
'__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__
', '__set__', '__setattr__', '__str__', 'fdel', 'fget', 'fset']

Ah! So all I have to do is call fget():

>>> col.selectedColors[0].fget()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: getSaturatedColor() takes exactly 1 argument (0 given)

Of course, because it needs a “self” argument. I have one (‘col’) but its not being used in the context of the call to fget. Lets try passing it in:

>>> col.selectedColors[0].fget(col)
(216.75, 255.0, 255.0)

Ah! So now I need a way to unmangle each element in the list, at the time the list is accessed.. what could I possibly use build the list then? I know, property()!

and thus we have tupleProperty. like tintedColor, we need to essentially curry some state to the property call, so we need to wrap it with a function.

    def tupleProperty(*args):
        def demangledTupleGetter(self):
            return [val.fget(self) for val in args]
        return property(demangledTupleGetter)

now, we can redefine defaultColors as appropriate:

    defaultColors = tupleProperty(gradientLeft, gradientRight, outlineColor, textColor)

what’s particularly neat is that we can change the hue and the right properties will be called:

>>> col.defaultColors
[(153.0, 255.0, 255.0), (204.0, 255.0, 255.0), (127.5, 255.0, 255.0), 
(50.489999999999995, 153.0, 153.0)]
>>> col.eventHue = 0.0
>>> col.defaultColors
[(255.0, 153.0, 153.0), (255.0, 204.0, 204.0), (255.0, 127.5, 127.5), 
(153.0, 50.489999999999995, 50.489999999999995)]

I think the only bummer here is that I had to dive into the internals of python in order to make use of fget() and untangle this duple/property dependency.

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