"Data" vs. "Non-data" Descriptors in PEP 252, and "once" attributes

Phillip J. Eby pje at telecommunity.com
Mon Feb 25 04:01:25 CET 2002

One nice feature of 2.2 is that you can define attribute descriptors in 
Python.  I've found a way to use this to create "once" attributes: 
instance attributes which are computed once per instance, with the value 
cached from that point on.  Here's an example::

    class once(object):

        def __init__(self,name,func):
            self.name = name
            self.func = func

        def __get__(self, ob, typ=None):
            if ob is not None:
                value = self.func(ob)
                ob.__dict__[self.name]= value
                return value

    class aThing(object):

        def anAttr(self):
            print "computing anAttr"
            return 100*100

        anAttr = once('anAttr', anAttr)

    thing = aThing()

    # prints "computing anAttr", then 10000
    print thing.anAttr

    # just prints 10000 the second time
    print thing.anAttr

Interestingly, this does *not* work if you try to do something similar 
with the built-in 'property' type.  Apparently, 'property' is considered 
a data descriptor, while the above 'once' type is considered a "non-data" 

Does anyone know if this usage is safe for future Python versions?  In 
another variation of this, I use a metaclass to make classes that 
automatically instantiate themselves inside of a containing class, e.g::

    class onceClass(type):
        def __get__(klass, ob, typ=None):
            if ob is not None:
                instance = klass(ob)
                ob.__dict__[klass.__name__] = instance
                return instance
    class Outer(object):

        class Inner:
            __metaclass__ = onceClass

            def __init__(self, ctx):
                print "creating Inner, bound to", ctx

    anOuter = Outer()

    # prints creation message, then the inner object
    print anOuter.Inner

    # just prints the inner object
    print anOuter.Inner

This sort of thing is handy for when you have complex (but relatively 
static) object structures that need to be built up.  I imagine it will 
come in especially handy for GUI programming, but I haven't actually used 
it for that yet.

Anyway, I'd like to be sure I'm not doing anything which is "unsafe" for 
future versions.  Although PEP 252 spells out most of the rules quite 
clearly, there is one bit of ambiguity: the definition of "data" versus 
"non-data" descriptors.  To be precise, it doesn't really define the 
difference.  The 2.2 source operationally defines a data descriptor as 
one which has a 'tp_descr_set' slot value, and new-style classes don't 
set that slot unless you supply a '__set__' method.  (Interestingly, 
'__delete__' is ignored for slot setup purposes, and supplying 
'__delete__' without a '__set__' still results in a "non-data" 

Operationally, it seems it would be hard to change any of this in a way 
that would break my "once" examples without also breaking lots of other 
things.  But the PEP doesn't really give an official way to create either 
a data or non-data descriptor; it just says what the current 
implementation does.  Hence my concern.

Of course, if upon reading this, lots of people think that "once" 
attributes are cool and start using it, I suppose it'll become a backward 
compatibility issue for more than just me.  :)

More information about the Python-list mailing list