[Zope3-dev] Re: Interface support in Python? (was Re: [Zope3-dev] Proposal: Improving on __implements__)

Phillip J. Eby pje@telecommunity.com
Thu, 23 Jan 2003 09:36:49 -0500


Your experiment doesn't run far enough or do the right things to show you 
the really interesting bits...  Like that accessing 'c.y' will not call 
m.y.__get__, but only c.y.__get__.  But, if you add a __set__ method to 
'dualattr', it *will* call m.y.__get__, and not c.y.__get__.

I still haven't figured out how to really explain the problem, 
though.  :(  I ran afoul of this issue in some work of my own, where I 
needed to keep track of metadata for both a metaclass and its instances, in 
a uniform way.  Implementing a dual descriptor is only "easy" when there 
are no metaclasses involved, *or* if a metaclass' instances can all have 
the same value for the metadata (i.e. a metaclass attribute).  The problem 
stems from the fact that a class' dictionary is also used to provide 
descriptors for its instances; thus, you can't store the *actual data* in 
the same place, because it otherwise becomes a descriptor for that class' 
instances!

Now, if what you're proposing is to create fresh descriptor instances for 
every class and metaclass, that contain the interface assertions in 
themselves, rather than acting as accessor/mutators for data stored in the 
classes or instances, then yes, I think that could work.

It would not be able to be done in a way that was backward compatible with 
existing use of '__implements__', however, and that means that third party 
code based on the existing Interface package needs a migration period where 
we can use the new spelling to specify '__implements__' and 
'__class_implements__', while still being compatible, before actually 
getting rid of '__implements__' and '__class_implements__'.


At 07:25 AM 1/23/03 -0500, Jim Fulton wrote:
>You made me perform the followeing experiment.
>
>I wrote the script:
>
>-------------------------------------------
>
>class dualattr(object):
>
>     def __init__(self, ival, cval):
>         self.ival = ival
>         self.cval = cval
>
>     def __get__(self, inst, class_):
>         if inst is None:
>             return self.cval
>         else:
>             return self.ival
>
>
>class m(type):
>     mm = 'mm'
>     mc = 'mc'
>     mi = 'mi'
>
>     x = dualattr("cx", "mx")
>     y = dualattr("cy", "my")
>
>class c:
>     __metaclass__ = m
>
>     cc = 'cc'
>     ci = 'ci'
>
>     mc = 'cmc'
>     mi = 'cmi'
>
>     y = dualattr("iy", "cy")
>     z = dualattr("iz", "cz")
>
>i = c()
>
>i.ii = 'ii'
>i.ci = 'ici'
>i.mi = 'imi'
>
>print 'i.mm', getattr(i, 'mm' ,'AttributeError')
>print 'i.mc', getattr(i, 'mc' ,'AttributeError')
>print 'i.mi', getattr(i, 'mi' ,'AttributeError')
>print 'i.cc', getattr(i, 'cc' ,'AttributeError')
>print 'i.ci', getattr(i, 'ci' ,'AttributeError')
>print 'i.ii', getattr(i, 'ii' ,'AttributeError')
>print 'i.x', getattr(i, 'x' ,'AttributeError')
>print 'i.y', getattr(i, 'y' ,'AttributeError')
>print 'i.z', getattr(i, 'z' ,'AttributeError')
>print
>
>print 'c.mm', getattr(c, 'mm' ,'AttributeError')
>print 'c.mc', getattr(c, 'mc' ,'AttributeError')
>print 'c.mi', getattr(c, 'mi' ,'AttributeError')
>print 'c.cc', getattr(c, 'cc' ,'AttributeError')
>print 'c.ci', getattr(c, 'ci' ,'AttributeError')
>print 'c.x', getattr(c, 'x' ,'AttributeError')
>print 'c.y', getattr(c, 'y' ,'AttributeError')
>print 'c.z', getattr(c, 'z' ,'AttributeError')
>print
>
>print 'm.mm', getattr(m, 'mm' ,'AttributeError')
>print 'm.mc', getattr(m, 'mc' ,'AttributeError')
>print 'm.mi', getattr(m, 'mi' ,'AttributeError')
>print 'm.x', getattr(m, 'x' ,'AttributeError')
>print 'm.y', getattr(m, 'y' ,'AttributeError')
>print
>
>i.mm
>
>-------------------------------------------
>i.mm AttributeError
>i.mc cmc
>i.mi imi
>i.cc cc
>i.ci ici
>i.ii ii
>i.x AttributeError
>i.y iy
>i.z iz
>
>c.mm mm
>c.mc cmc
>c.mi cmi
>c.cc cc
>c.ci ci
>c.x cx
>c.y cy
>c.z cz
>
>m.mm mm
>m.mc mc
>m.mi mi
>m.x mx
>m.y my
>
>Traceback (most recent call last):
>   File "t.py", line 69, in ?
>     i.mm
>AttributeError: 'c' object has no attribute 'mm'
>
>-------------------------------------------
>
>which seems pretty reasonable.
>
>Note that the dualattr descriptor illustrates how the __implements__ 
>descriptor
>would work. With your proposal, given a custom meta class, there would be two
>ways of saying what the interface if a class is. You use "implements" in the
>meta class, or you can say "classImplements" in the class.
>
>>Maybe a change in the way getattr and metaclasses work could accompany a 
>>proposal to get interfaces into Python?
>
>I don't think this is necessary.
>
>Jim
>
>--
>Jim Fulton           mailto:jim@zope.com       Python Powered!
>CTO                  (888) 344-4332            http://www.python.org
>Zope Corporation     http://www.zope.com       http://www.zope.org