Setting up properties from the __init__ method

Bengt Richter bokr at oz.net
Thu Oct 30 01:41:18 EST 2003


On Thu, 30 Oct 2003 11:48:10 +1100, Andrew Bennetts <andrew-pythonlist at puzzling.org> wrote:

>On Thu, Oct 30, 2003 at 12:22:09AM +0000, user at domain.invalid wrote:
>> 
>> So is saying self.attr in the __ini__ method different
>> than just saying attr=value right in the class definition?
>
>Yes; assigning to self.attr in a method (such as __init__) will set that
>attribute on the instance, but assigning in the class definition will set
>that attribute on the class.
Unless it happens to be defined in the class as a property, in which case that
will be triggered ;-) (BTW, you can use that to good effect in an __init__ or
other method to access a property consistently from everywhere. See example, where
the __init__ accesses the self.prop both ways.
>
>> Assigning a the return of property() behaves clearly differently
>> in each case.
>
>Descriptors (such as properties) need to be set on a class to work their
>magic.  Instances don't check their attributes for __get__/__set__/__del__
>magic methods when accessing them, but classes do.
>
>(This question pops up fairly regularly, and it's not obvious to new users
>what's going on... perhaps there should be a FAQ entry about why
>per-instance properties don't work?)
>
You can program a normal property to delegate to instance-specific stuff though, so you
could fake it pretty well if you put your mind to it ;-) The question would be WTHeck you
were trying to do with that kind of design. Ok, we won't break our toys, but we can mess
with them a little to see how they work, e.g.,

 >>> class Foo(object):
 ...     def getprop(self):
 ...         instprop = vars(self).get('prop')
 ...         if instprop: return instprop.fget(self)
 ...         return '%r from getprop'%self._prop
 ...     def setprop(self, v): self._prop=v
 ...     prop = property(getprop, setprop)
 ...     def __init__(self):
 ...         self.prop = '<initial prop value>'  # note that this goes via setprop to self._prop
 ...         def instgetprop(self): return '%r from instgetprop'%self._prop
 ...         self.__dict__['prop'] = property(instgetprop)   # self.prop would trigger setprop
 ...
 >>> foo = Foo()
 >>> foo.prop
 "'<initial prop value>' from instgetprop"

Note that the above is deferring to the instance-attribute-defined prop

 >>> foo.prop = 123
 >>> foo.prop
 '123 from instgetprop'

... still doing it. Now we'll get rid of the instance attribute named prop

 >>> del foo.prop # can't get rid of instance property this way
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 AttributeError: can't delete attribute

That exception was because the property intercepts get set and del, and we didn't
define the latter property function.

 >>> del foo.__dict__['prop'] # this way doesn't trigger class-defined prop

Ok, now to access prop without the instance attribute, so the real prop (which always
gets control) doesn't see anything to defer to:

 >>> foo.prop
 '123 from getprop'

QED

 >>> foo.prop = 456
 >>> foo.prop
 '456 from getprop'

Now if we re-establish the instance prop by re-executing __init__:

 >>> foo.__init__
 <bound method Foo.__init__ of <__main__.Foo object at 0x009010F0>>
 >>> foo.__init__()

We get the delegated effect (that we programmed the real prop to do) again

 >>> foo.prop
 "'<initial prop value>' from instgetprop"
 >>> foo.prop = 789
 >>> foo.prop
 '789 from instgetprop'

You could do much more generalized stuff along the same lines, intercepting any attribute
name and checking for an "instance property" also. Only your imagination (and possibly backlash
from your code maintenance heirs ;-) sets the limits.
 
This sort of stuff is not recommended for ordinary use, but it's nice to know you can do
about anything you want with Python if you have a real need.
However, most such "needs" are probably mistaken ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list