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