The right way to 'call' a class attribute inside the same class
steve+python at pearwood.info
Sun Dec 11 20:34:04 EST 2016
On Mon, 12 Dec 2016 07:10 am, Juan C. wrote:
> I'm watching a Python course and was presented a topic regarding classes.
> One of the examples were:
> class Box:
> serial = 100
> def __init__(self, from_addr, to_addr):
> self.from_addr = from_addr
> self.to_addr = to_addr
> self.serial = Box.serial
> Box.serial += 1
If you want to distinguish between an instance attribute and a class
attribute, you must specify the instance or the class:
self.serial # may be the instance attribute, or the class attribute
Box.serial # always the class attribute
But what happens inside a subclass?
Generally we expect methods called from the subclass BoxWithLid to refer to
the BoxWithLid attribute, not the Box attribute. So Box.serial will be
wrong when the method is called from a subclass.
type(self).serial # still correct when called from a subclass
> The instructor said that the right way to call a class attribute is to use
> 'Class.class_attr' notation,
That is nearly always wrong, since it will break when you subclass. If you
do that, you should document that the class is not expected to be
subclasses, and may not work correctly if you do.
> but on the web I found examples where people
> used 'self.class_attr' to call class attributes. I believe that using the
> first notation is better ('Class.class_attr'), this way the code is more
> explicit, but is there any rules regarding it?
Assignment to self.serial will always create or affect an instance
attribute. But merely retrieving self.serial will use inheritance to try
returning the instance attribute *if it exists*, and if not, fall back on
the class attribute (or a superclass).
This is especially useful for read-only defaults, constants or configuration
page_size = A4
dimensions = list(self.page_size)
dimensions -= self.left_margin
dimensions -= self.right_margin
dimensions -= self.top_margin
dimensions -= self.bottom_margin
doc = Document() # uses the default page size of A4
doc.page_size = Foolscape # override the default
It is a matter of taste and context whether you do this, or the more
self.page_size = A4
Use whichever is better for your specific class.
So... in summary:
When *assigning* to an attribute:
- use `self.attribute = ...` when you want an instance attribute;
- use `Class.attribute = ...` when you want a class attribute in
the same class regardless of which subclass is being used;
- use `type(self).attribute = ...` when you want a class attribute
in a subclass-friendly way.
When *retrieving* an attribute:
- use `self.attribute` when you want to use the normal inheritance
rules are get the instance attribute if it exists, otherwise a
class or superclass attribute;
- use `type(self).attribute` when you want to skip the instance
and always return the class or superclass attribute.
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.
More information about the Python-list