[Python-ideas] Assignment decorators (Re: The Descriptor Protocol...)

Greg Ewing greg.ewing at canterbury.ac.nz
Fri Mar 4 06:11:01 CET 2011


Guido van Rossum wrote:

>>* OverridableProperty instances in PyGUI
>
> So a metaclass doesn't work for the latter?

It could be made to work, but it would be overkill and
introduce an unnecessary and undesirable coupling between
the property and the class that it resides in.

> It is worse, because undecorated function and class definitions also
> have that restriction; but undecorated assignments don't.

The 'def' version would address that, because it wouldn't
be surprising for it to inherit the same restrictions as
'def' for functions.

> Hm. Maybe that's because [SqlAlchemy's] metaclass has some other side effects
> that has nothing to do with the property definition patchup?

No, it's directly related. Whenever a Table subclass is created,
the metaclass goes looking for field definitions, and if it
doesn't find any it throws an exception. That's fine if you're
defining an actual table, but it also happens if you're just
trying to create a specialised version of Table that you will
then further subclass to create actual tables.

The root of the problem is that two different things -- creating
a new kind of Table and creating an actual Table -- are both
done the same way, by subclassing, and the metaclass can't
distinguish between them.

In this particular case, there's a fairly straightforward
solution that could have been used (but wasn't): if there are
no fields, simply do nothing instead of raising an exception.
But in general it illustrates that throwing large amounts of
magic around can easily lead to unintended consequences.

> With enough patience you can actually combine metaclasses using
> multiple inheritance. ...
> in Python you have to work at it a little harder,

It's *harder* in Python than it is in C++? Somehow that fails
to fill me with confidence. :-(

> I guess you need to enlighten me more about OverridableProperty;

It's quite simple, really. It turns accesses to a property called
'xxx' into calls of methods called 'get_xxx' and 'set_xxx' on the
containing class. Subclasses can then override those methods, make
super calls, etc. instead of having to mess around building a new
property and extracting __get__ and __set__ methods out of the
inherited property. It's defined like this:

def overridable_property(name, doc = None):
     """Creates a property which calls methods get_xxx and set_xxx of
     the underlying object to get and set the property value, so that
     the property's behaviour may be easily overridden by subclasses."""

     getter_name = intern('get_' + name)
     setter_name = intern('set_' + name)
     return property(
         lambda self: getattr(self, getter_name)(),
         lambda self, value: getattr(self, setter_name)(value),
         None,
         doc)

It needs to know the name of the property so that it can build
the correct method names. That's an implementation detail that
the user shouldn't have to care about, but I'm forced to expose
it because of a deficiency in the language. It's like a house
with pieces of plumbing sticking out of the walls instead of
being hidden away. It works, but it's ugly and annoys you every
time you look at it.

I *could* hide the plumbing by using a metaclass, but then I
would be restricted to using these properties only in classes
having that metaclass. That would be disappointing, because
the property itself is quite self-contained and will happily
work in any class.

It would also cause me considerable headaches if I wanted to
use them in a class already having a custom metaclass, such
as a Django model or an SqlAlchemy table. If I studied the
metaclass in question closely enough, I might be able to find
a way to merge them together, but I don't want to have to do
that. Arcane details of someone else's metaclass are the *last*
thing I want to be bothered with!

> ISTM that passing the name in as a string literal seems
> a pretty small price to pay for the benefit derived,

It's a small price each time you pay it, yes, but it mounts
up. Every time I write one I think "There must surely be a
better way of doing this... oh, no, that's right, there isn't."

If the ancient Chinese had had computers, I'm sure they would
have invented a form of torture based on making programmers
repeatedly solve problems that the language doesn't *quite*
have the tools to do elegantly...

-- 
Greg



More information about the Python-ideas mailing list