__init__ & attributes clarification

Alex Martelli aleax at aleax.it
Mon Feb 3 04:03:47 EST 2003


Bengt Richter wrote:

> On Mon, 03 Feb 2003 07:40:50 GMT, Alex Martelli <aleax at aleax.it> wrote:
> [...]
>>
>>class Subtler:
>>    xx = 23
>>    def setinself(self): setattr(self, 'xx', 45)
>>    def setinclass(self): setattr(Subtler, 'xx', 67)
>>
> [...]
> 
> Typographically, I like the above way of spelling Subtler,
> but I don't like the dependency on the global binding
> of the Subtler name, so I've taken to using self.__class__
> instead in contexts like that. Do you have any strong
> feelings one way or the other?

Relatively-concise answer:

I generally prefer to use self.__class__ in most such 
contexts, because I generally *WANT* to enable "data
override" upon inheritance.  However, the semantics are
so hugely different (as soon as any inheritance does
happen) that I would not base my choice on secondary
issues such as the risk that name Subtler could be
rebound in global scope -- consider that such rebinding
inevitably breaks all "delegation to superclass" within
method calls (be it with 'super' or with the more
traditional approach of using an unbound method), and
thus such rebinding is known to be a huge risk anyway.

With details better spelled out:

The semantics of the alternative:

class Subtlest:
    xx = 23
    def setinself(self): setattr(self, 'xx', 45)
    def setinclass(self): setattr(self.__class__, 'xx', 67)

are VERY different from those of class Subtler, quite
apart from the risk that somebody could be rebinding
class names in global scope later.

The difference in semantics appears as soon as you
use inheritance:

class Sr1(Subtler): pass
class St1(Subtlest): pass

a = Subtler(); a1 = Sr1()
b = Subtlest(); b1 = St1()

a1.setinclass()
b1.setinclass()
print a.xx, b.xx

Clearly, the call a1.setinclass() DOES affect the value
seen as a.xx, while the call b1.setinclass() DOES NOT
affect the value seen as b.xx.  In other words, with
class Subtler, the intention is that all instances of
it AND ANY SUBCLASS all share the SAME attribute 'xx';
with class Subtlest, the intention is that instances
of EACH DISTINCT SUBCLASS share a separate 'xx' (in
each case, of course, only for instances that do not
have a per-instance 'xx' at some point in time).

I think the latter ("data overriding") is somewhat
more frequent than the former.  But anyway, they ARE
very different, therefore the choice should be based
on which of the two semantics you want, rather than
on the marginal risk that somebody might rebind the
names of the class to mean something else in global
scope.

Why I think this renaming risk is marginal: because
it WOULD inevitably break other things.  A method
needs to mention a class explicitly (and cannot
resort to self.__class__) in at least one frequent
and important case, namely, when the method is
overriding a base class's implementation and needs
to call that implementation too -- then, whether
by the new approach super(MyClass, self).foo(), or
the old one BaseClass.foo(self), explicit mention
of a class name is inevitable.

Thus, it's KNOWN that rebinding class names in
global scope is prone to break things; therefore,
people don't do it.  Hence, it's not a huge risk
one should worry about overmuch when coding one's
classes and methods, IMHO.


Alex





More information about the Python-list mailing list