ClassName.attribute vs self.__class__.attribute

  • Thread starter Gabriel Rossetti
  • Start date
G

Gabriel Rossetti

Hello everyone,

I had read somewhere that it is preferred to use
self.__class__.attribute over ClassName.attribute to access class (aka
static) attributes. I had done this and it seamed to work, until I
subclassed a class using this technique and from there on things started
screwing up. I finally tracked it down to self.__class__.attribute! What
was happening is that the child classes each over-rode the class
attribute at their level, and the parent's was never set, so while I was
thinking that I had indeed a class attribute set in the parent, it was
the child's that was set, and every child had it's own instance! Since
it was a locking mechanism, lots of fun to debug... So, I suggest never
using self.__class__.attribute, unless you don't mind it's children
overriding it, but if you want a truly top-level class attribute, use
ClassName.attribute everywhere!

I wish books and tutorials mentioned this explicitly....

Gabriel
 
B

bruno.desthuilliers

Hello everyone,

I had read somewhere that it is preferred to use
self.__class__.attribute over ClassName.attribute to access class (aka
static) attributes.

It's even prefered to use self.attribute, unless you know you have
both an instance and a class attribute by the same name (but then if
you need to know which one you're accessing then you have a serious
design problem).
I had done this and it seamed to work, until I
subclassed a class using this technique and from there on things started
screwing up. I finally tracked it down to self.__class__.attribute! What
was happening is that the child classes each over-rode the class
attribute at their level,

Which is why it's prefered to access the attribute from the class (or
more simply from the instance which will get them from the class) - a
subclass may have extremly good reasons to have it's own instance of
the attribute.
and the parent's was never set,

This is another problem.
so while I was
thinking that I had indeed a class attribute set in the parent, it was
the child's that was set, and every child had it's own instance! Since
it was a locking mechanism, lots of fun to debug...

I can well believe it, and you do have my whole sympathy.
So, I suggest never
using self.__class__.attribute, unless you don't mind it's children
overriding it, but if you want a truly top-level class attribute, use
ClassName.attribute everywhere!

I would not have expressed it that way. My own experience is that,
most of the time, you do know when it's ok for the subclasses to have
their own instance of the attribute and when it's not, so in the (very
rare) cases it's not ok you use __name_mangling.

Now I'd say that *unknowingly* overriding an attribute of your own
base class when you didn't expect it to happen is mostly a sign that
there's something you don't (didn't ?) quite get wrt/ lookup /
assignment / namespace etc rules in Python.
I wish books and tutorials mentioned this explicitly....

Possibly. OTHO, if you understand Python's lookup (name resolution)
rules, the difference between MyBaseClass.attrib and
self.__class__.attrib should be obvious. But this surely could be a
good example in a tutorial !-)
 
H

Hrvoje Niksic

It's even prefered to use self.attribute,

I was going to write exactly that, but it occurred to me that he might
want to be *assigning* to self.__class__.attribute (or
HisClass.attribute) from his methods, in which case self.attribute
would be quite different.
 
G

Gabriel Rossetti

Larry said:
If you define a class instance variable with the same name as the
class attribute, how would Python be able to distinguish the two?
That is a feature not a problem. Getter looks for instance attribute,
if one is not found it looks for a class attribute, and upwards. This
behavior is used by Zope to do all sorts of neat stuff.

-Larry Bates
A class instance variable, you must mean an instance attribute no? If
that is so, then with just self.attribute? Maybe there is a concept that
I don't know about, I've studied class/static attributes and instance
attributes in my OOP classes.

Gabriel
 
B

Bruno Desthuilliers

Hrvoje Niksic a écrit :
I was going to write exactly that, but it occurred to me that he might
want to be *assigning* to self.__class__.attribute (or
HisClass.attribute) from his methods, in which case self.attribute
would be quite different.

ACK.
 
B

Bruno Desthuilliers

Gabriel Rossetti a écrit :
A class instance variable, you must mean an instance attribute no?

I think that's what he meant, yes.
If
that is so, then with just self.attribute? Maybe there is a concept that
I don't know about,

The concept of name resolution (aka lookup) rules in Python, perhaps ?
When you do obj.attribute, attribute is first looked for in the object,
then in it's class, then in the parent classes. So yes, you can get a
class (or parent class) attribute directly on the instance. Note that
assignment on the instance (obj.attribute = value) will alway (computed
attributes or other hooks excepted) create the attribute on the target,
so if you have Class.attribute set, and then do obj = Class();
obj.attribute = 42, then obj.attribute will shadow Class.attribute.
I've studied class/static attributes and instance
attributes in my OOP classes.

Forget about your OOP classes, mostly if it was in fact a Java or C++
class. Python's object model is very far away from what most courses
present as "OOP".
 
M

Mike Orr

Hello everyone,

I had read somewhere that it is preferred to use
self.__class__.attribute over ClassName.attribute to access class (aka
static) attributes. I had done this and it seamed to work, until I
subclassed a class using this technique and from there on things started
screwing up. I finally tracked it down to self.__class__.attribute! What
was happening is that the child classes each over-rode the class
attribute at their level, and the parent's was never set,

That's a misunderstanding of classes vs instances. If you have an
instance of MyClass(Superclass), there is one instance but several
classes. The instance is of MyClass; there is no instance of
Superclass. 'self' has a .__class__ attribute because it's an
instance, but MyClass and Superclass do not because they're already
classes.

Going further, self.a retrieves the instance attribute if it exists,
or or MyClass.a if it doesn't, or Superclass.a if that doesn't. But
assigning to self.a always assigns to an instance attribute. To
assign a class attribute you must use .__class__ or TheClass.
Likewise, to retrieve a class attribute that has been overridden by a
subclass or instance, you must use .__class__ or TheClass.

There's a very good reason to use self.__class__: it makes it possible
to subclass your class. In Jason Orendorff's path.py, some path
methods return new path objects. He uses 'path()' rather than
self.__class__ to construct these. So if you subclass 'path' to
extend it, these methods return path objects rather than your subclass
objects. In my Unipath package which was written later, I use
self.__class__ in these cases to allow users to extend my class.

It's a little annoying that if you want to print a class's name in
some unknown object, you have to use obj.__class__.__name__ if it's an
instance, and obj.__name__ if it's a class. I sometimes wish classes
had a .__class__ attribute that's the class itself, but I can see how
that would cause its own confusion (and recursion).

-- Mike Orr <[email protected]>
 
G

Gabriel Rossetti

Mike said:
That's a misunderstanding of classes vs instances. If you have an
instance of MyClass(Superclass), there is one instance but several
classes. The instance is of MyClass; there is no instance of
Superclass. 'self' has a .__class__ attribute because it's an
instance, but MyClass and Superclass do not because they're already
classes.

Yes, I know that
Going further, self.a retrieves the instance attribute if it exists,
or or MyClass.a if it doesn't, or Superclass.a if that doesn't. But
assigning to self.a always assigns to an instance attribute. To
assign a class attribute you must use .__class__ or TheClass.
Likewise, to retrieve a class attribute that has been overridden by a
subclass or instance, you must use .__class__ or TheClass.

There's a very good reason to use self.__class__: it makes it possible
to subclass your class. In Jason Orendorff's path.py, some path
methods return new path objects. He uses 'path()' rather than
self.__class__ to construct these. So if you subclass 'path' to
extend it, these methods return path objects rather than your subclass
objects. In my Unipath package which was written later, I use
self.__class__ in these cases to allow users to extend my class.
Ok, I see a use for that now, I also tried a minimal example of my
problem and it worked as I expected, and thus I am unable to reproduce
my problem outside of my code. It may be linked to the fact that I am
using Zope interfaces and Tisted's plugin mechanism, as this problem was
born in that context ; it's possible that something happens in the
background that makes it behave strangely. Basically, I the base class
for the plugins create a class attribute of an object that is a mutex
and each subclass solicited it for access to the I/O. The class
attribute was created only once in it's __init__ (using a conditional
test). After running it though a debugger, I saw that in reality, every
Child instantiated it, so every child had it's own mutex, thus they
could each access the I/O even if it was "supposed" to be locked. I had
been using "self.__class__.myMutex" everywhere, so I changed it to
"MyClass.myMutex" and the code behaved correctly. This is what prompted
me to write this thread. As i said before, I tried reproducing the
problem out of context, with just regular classes, no interfaces &
plugin mechanism, and it works as I had expected (originally).
It's a little annoying that if you want to print a class's name in
some unknown object, you have to use obj.__class__.__name__ if it's an
instance, and obj.__name__ if it's a class. I sometimes wish classes
had a .__class__ attribute that's the class itself, but I can see how
that would cause its own confusion (and recursion).

Yes :)
-- Mike Orr <[email protected]>
Gabriel
 
G

Gabriel Rossetti

Bruno said:
Gabriel Rossetti a écrit :

I think that's what he meant, yes. Ok.

The concept of name resolution (aka lookup) rules in Python, perhaps ?
When you do obj.attribute, attribute is first looked for in the
object, then in it's class, then in the parent classes. So yes, you
can get a class (or parent class) attribute directly on the instance.
Note that assignment on the instance (obj.attribute = value) will
alway (computed attributes or other hooks excepted) create the
attribute on the target, so if you have Class.attribute set, and then
do obj = Class(); obj.attribute = 42, then obj.attribute will shadow
Class.attribute.
Not really, see my answer to Mike Orr's msg.
 
H

Hrvoje Niksic

Mike Orr said:
There's a very good reason to use self.__class__: it makes it
possible to subclass your class.

This really depends on the usage. In the OP's use case, he wanted the
subclasses to share the same lock object defined in the superclass
(because of synchronization), so it would have been wrong to use
self.__class__ *because* of subclassing.

Also note that for new-style classes, self.__class__ can be spelled as
type(self), since there is no distinction between classes and types.
It's a little annoying that if you want to print a class's name in
some unknown object, you have to use obj.__class__.__name__ if it's
an instance, and obj.__name__ if it's a class. I sometimes wish
classes had a .__class__ attribute that's the class itself, but I
can see how that would cause its own confusion (and recursion).

They do, the metaclass. :)
 
B

Bruno Desthuilliers

Mike Orr a écrit :
(snip)
'self' has a .__class__ attribute because it's an
instance, but MyClass and Superclass do not because they're already
classes.

Not true for new-style classes:
<type 'type'>


(snip)
I sometimes wish classes
had a .__class__ attribute that's the class itself,

newstyle classes do have a class attribute, that refers to the metaclass.
but I can see how
that would cause its own confusion (and recursion).

FWIW, metaclasses do have a class attribute that refers to itself !-)
 
H

Hrvoje Niksic

Duncan Booth said:
In fact, thinking about it a bit more, I think that if you did have
another metaclass which is its own metaclass then the class cannot
subclass 'object'.

You could if the metaclass of your metaclass inherited from 'type'.
Then your types could still be binary-compatible with Python types,
and your objects with 'object'. It's difficult to envision what you'd
gain with a custom meta-meta-class, but it seems possible:
.... __metaclass__ = MetaMeta
........ __metaclass__ = Meta
....True
 
G

Gabriel Rossetti

Duncan said:
Classes are also instances, usually they are instances of the type 'type'
(and even 'type' is an instance of itself):

Ok, I didn't know that
<type 'type'>

Old style classes don't have a class attribute, but you shouldn't be using
old style classes anyway and so long as you use
type(x)
to access its class rather than accessing the __class__ attribute directly
that doesn't particularly matter.
Yes, I don't use old style classes
 
B

Bruno Desthuilliers

Duncan Booth a écrit :
One metaclass (i.e. type) has a class attribute that refers to itself.
>
Other metaclasses have a class attribute that refers to the metaclass's
metaclass. I can't think of any situation where a metaclass would be its
own metaclass except for 'type' itself, but then I think I've got a
headache trying to think about this

Yeps, same pattern here :-/

Thanks for the correction, anyway.
(snip)
 
T

Terry Reedy

"> One metaclass (i.e. type) has a class attribute that refers to itself.
>
Other metaclasses have a class attribute that refers to the metaclass's
metaclass. I can't think of any situation where a metaclass would be its
own metaclass except for 'type' itself, but then I think I've got a
headache trying to think about this

In 3.0, with old classes gone and all classes called 'classes'*:

All objects are instances of base class 'object', nearly always indirectly
via subclasses of 'object'. All classes are subclasses of 'object',
including base metaclass 'type'. The methods of 'object' are the methods
that apply to objects as objects in general, as instances. Many of these
are 'base methods' that get overridden in subclasses to fit their
instances.

All classes (including object and type, dropping the quotes) are instances
of type, usually directly but occassionally via subclasses of type. In
other words, the subclass type defines the subclass of objects that are
classes. The additional attributes and methods of type (beyond those
inherited from object ) are, therefore, those that specifically apply to
all classes. These include __call__, by which classes (including 'object'
and 'type' itself) make instances.

The circularity conundrum is that type is a subclass of object while the
immutable class attribute of both object and type is type. The answer, of
course, is that a Python interpreter, as god of a Python universe, creates
both 'ex nihilo', from nothing, with the required attributes.

* In 3.0a5+<class 'type'> # instead of <type 'type'>, and so on.

Terry Jan Reedy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top