properties and formatting with self.__dict__
Alex Martelli
aleax at aleax.it
Sat Feb 22 03:27:39 EST 2003
Andrew Bennetts wrote:
>> > I think you could fairly easily write your own instanceproperty that
>> > did work with instances, though.
>>
>> Could you? Maybe I'm having a blind spot, but, without having a
>> custom metaclass (redefining __getattribute__ differently from the
>> implementation in object), how would you do it? I.e. how would you
...
>> would result in x.getfullname() being _called_? I just can't
>> see how, and I'd like to learn -- thanks!
>
> Hmm. It's harder than I thought :)
Ah -- pity!
> I naïvely assumed that descriptors worked equally well for instances
> as for classes, which turns out not to be true. If it was, it would
> indeed be fairly easy...
Hmmm, wouldn't such a change cause breakage elsewhere?
> So I was wrong -- you'd have to define custom metaclass, or something
> along
> those lines. You could probably avoid a metaclass with something like:
>
> class C:
> def __init__(self, ...):
> instanceproperty(self, 'fullname', self.getfullname)
>
> where instanceproperty was a function that inserted some sort of evil
> descriptor into C.fullname if there wasn't already one... but it'd be
> worse than simply defining __getattribute__ or __getattr__.
It wouldn't be a per-instance descriptor if it changed the class, though.
It's either a way for instances to trip on each other (when you define
the second istance ITS self.getfullname bound method gets called
even for the already-existing first instance) or just a weird way to
insert a normal-ish descriptor in a class, no?
> Hmm... maybe you could abuse metaclasses to return a freshly minted class
> object instead of a mere instance, so that descriptors would work....
>
> [experiments]
>
> Ok, you could do it like this:
>
> >>> class instanceIsClass(object):
> ... def __new__(cls):
> ... class Instance(cls): pass
> ... return Instance
> ...
> >>>
> >>> class C(instanceIsClass):
> ... def __new__(self):
> ... self.fullname = property(self.getfullname)
> ... return object.__new__(self)
> ...
> ... def getfullname(self):
> ... return 'Andrew Bennetts'
> ...
> >>> c = C()
> >>> c.fullname
> 'Andrew Bennetts'
>
> Which isn't too far removed from:
>
> class C:
> def __init__(self):
> self.fullname = property(self.getfullname)
>
> def getfullname(self):
> return 'Andrew Bennetts'
>
> Well, you'd have to stand a few metres back from the monitor and squint,
> but
> then it kinda sorta looks similar <wink>. Perhaps with a bit more
> cleverness you could simplify my hack more.
It may help to coat the monitor with semi-opaque substances too;-).
As for simplification, one clever hack may be to notice that, since
instanceIsClass only provides __new__ and C overrides it anyway,
instanceIsClass is playing no role:
class C(object):
def __new__(self):
self.fullname = property(self.getfullname)
return object.__new__(self)
def getfullname(self):
return 'Andrew Bennetts'
x=C()
print x.fullname
But of course the "self" in C.__new__ is C itself, so what we're
doing is setting a property on the *class* C, not on any one of its
instances. We're setting it a bit late, and redundantly setting it
again to a new instance of property with the same __get__
(which calls unbound method C.getfullname), but it is in no
sense a per-instance property...
> Thanks for helping me realise that I'd assumed wrongly :)
You're welcome -- this stuff IS subtle, so I really hoped I was the
one that had misunderstood something (would hardly be the first
time;-). Perhaps more interestingly, can you think of a way to
change object that WOULD let per-class properties work without
breaking something else? I can't. A custom metaclass that does
allow per-instance properties could not be used universally, it
seems to me. But you may have thought more deeply about it...
Alex
More information about the Python-list
mailing list