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