On Thu, 30 Oct 2003 11:48:10 +1100, Andrew Bennetts <an***************@puzzling.org> wrote:
On Thu, Oct 30, 2003 at 12:22:09AM +0000, us**@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