[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