[Python-3000] __special__ attrs looked up on the type, not instance

Thomas Wouters thomas at python.org
Thu Mar 15 01:02:18 CET 2007


On 3/15/07, Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
>
> Guido van Rossum wrote:
> > We could even do this by hacking the default getattr
> > implementation to skip the instance dict if the name starts and ends
> > with two underscores.
>
> But unless I'm mistaken, this behaviour is only
> appropriate for *methods*, and you can't tell
> just from whether the name has double underscores
> whether it's a method or not.


We can make __*__ methods be a different kind of method (by making the
metaclass wrap them up in a different kind of descriptor, one with __set__
set. Python treats descriptors with a __set__ slightly different than those
without __set__: it bypasses __dict__ entirely, for new-style classes. This
trick doesn't work for classic classes.)

For instance:

import types

class SpecialMethodDescr(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, inst, cls=None):
        # For backward compatibility, insert inst.__dict__ checks and warns
here (and we should get our name passed in.)
        return self.func.__get__(inst, cls)
    def __set__(self, inst, value):
        # Possibly allow assignment here, by assigning to inst.__dict__, but
warn about it not being used.
        raise AttributeError

class SMDType(type):
    def __new__(cls, name, bases, attrs):
        for attr in attrs:
            if (attr.startswith("__") and attr.endswith("__") and
                isinstance(attrs[attr], types.FunctionType)):
                attrs[attr] = SpecialMethodDescr(attrs[attr])
        return super(SMDType, cls).__new__(cls, name, bases, attrs)

class smd_object(object):
    __metaclass__ = SMDType

And to see it in action:

>>> class Old(object):
...   def __repr__(self):
...     return "Old.__repr__"
...
>>> class New(smd_object):
...   def __repr__(self):
...     return "New.__repr__"
...
>>> o = Old()
>>> n = New()
>>> def o_repr():
...   return "o_repr"
...
>>> o.__dict__['__repr__'] = o_repr
>>> def n_repr():
...   return "n_repr"
...
>>> n.__dict__['__repr__'] = n_repr
>>> o
Old.__repr__
>>> n
New.__repr__
>>> o.__repr__()
'o_repr'
>>> n.__repr__()
'New.__repr__'

-- 
Thomas Wouters <thomas at python.org>

Hi! I'm a .signature virus! copy me into your .signature file to help me
spread!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.python.org/pipermail/python-3000/attachments/20070315/f2cb887e/attachment.htm 


More information about the Python-3000 mailing list