New-style classes and special methods

Alex Martelli aleax at aleax.it
Tue Feb 25 03:42:23 EST 2003


Jp Calderone wrote:
   ...
>> def setSpecialAttr(inst, name, value):
>>     try:
>>         inst.__customized_subclass__
>>     except AttributeError:
>>         class subclass(inst.__class__):
>>             __customized_subclass__ = True
>>         inst.__class__ = subclass
>>     setattr(inst.__class__, name, value)
>> 
> 
>   Thanks, this is a neat trick, I'll have to keep it in mind.  My use case
> lies along different lines (I tell a lie, I have no use case, but if I
> did, it would lie along these...):
> 
>     class AccessError(AttributeError): pass
> 
>     class PromiscuousPolicy:
>         def allowAccess(self, name):
>             return True
> 
>     class Proxy(object):
>         def __init__(self, original, policy=PromiscuousPolicy()):
>             self.__original = original
>             self.__policy = policy
> 
>         def __getattribute__(self, name):
>             p = super(Proxy, self).__getattribute__('__policy')

I think (haven't tested) that this would fail due to name
mangling issue.  My advice: use ONE leading underscore, NOT
two, unless you're perfectly sure you know what you're doing.

>             if not p.allowAccess(name):
>                 raise AccessError
>             return getattr(
>                 super(Proxy, self).__getattribute__('__original'),
>                 name
>             )
> 
>   I'm not sure I see a way to apply the approach setSpecialAttr uses
>   here...
> Though, maybe Proxy could iterate over the attributes of 'original',

That's pretty hard -- 'original' could get its attributes dynamically
too, in its own __getattr__ or __getattribute__ -- so, for perfect
generality, you can't assume the ability to iterate over ALL of them.

Still, you surely CAN iterate over all SPECIAL names, since they're
a finite, predefined set.  This should suffice, since non-special
names can be handled as above.  You'll get into hairy problems if
you want to deal with 'original' potentially being a classic class
instance as well as a new-style class instance, etc, etc, but the
"custom class" approach (perhaps different custom classes in different
cases) can probably help anyway.

E.g., a QAD approach might be (untested):

class Proxy(object):

    def __new__(cls, original, policy=PromiscuousPolicy()):
        class custom_subclass(original.__class__):
            def __new__(cls): return object.__new__(cls)
            def __init__(cls): pass
            def __getattribute__(self, name):
                if not policy.allowAccess(name):
                    raise AccessError, name
                return original.__class__.__getattribute__(original, name)
        return custom_subclass()

Note that this trick exploits lexical scopes to access original and
policy, thus avoiding clashes of __getattribute__ semantics.  The
overrides of __new__ and __init__ in the custom_subclass should ensure
against unwelcome "multiple initialization" attempts.  No doubt there
is much play needed to make this right, but I hope the general idea
can still be of use.


Alex





More information about the Python-list mailing list