Class-level variables - a scoping issue

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Oct 10 08:52:24 CEST 2010


On Sat, 09 Oct 2010 22:30:43 -0700, John Nagle wrote:

> Here's an obscure bit of Python semantics which is close to being a bug:

"Obscure"? It's possibly the most fundamental aspect of Python's object 
model. Setting instance.attr assigns to the instance attribute, creating 
it if it doesn't exist. Getting instance.attr retrieves the instance 
attribute, or the class attribute, or a superclass attribute, whichever 
is found first.

As for it being a bug, or close to being a bug, can you tell me which 
specified behaviour it fails to match?


[...]
>      Notice what happened here.  Within "fn1", the first
> reference to "self.classvar" references the class-level version of
> "classvar".  The assignment overrides that and creates an object-level
> instance of "self.classvar". Further references to "self.classvar" in f1
> then reference the object-level "classvar"

I'm sorry, I don't quite understand what you mean by "object-level". 
They're *all* objects. The ints 1 and 2 are objects. The instances t1 and 
t2 are objects. The class t is an object. The global namespace is an 
object. Built-ins are objects. Which of these plethora of objects do you 
mean by "object-level"?

I'm going to take a guess that you probably mean to distinguish between 
class attributes and instance attributes. From the context, that seems 
likely. Am I right?



>      Creating another instance of t makes it clear that the
> class-level variable never changes.   To change it, it has to be
> referenced as "t.classvar".

Yes.


>      Python protects global variables from similar confusion
> by making them read-only when referenced from an inner scope without a
> "global" statement.  But that protection isn't applied to class-level
> variables referenced through 'self'. Perhaps it should be.

The protection to class attributes is applied. When writing to self.attr, 
you can't accidentally change a class attribute ("global to the class"). 
To change it for all instances of the class, you have to specifically 
assign to class.attr instead.

I maintain that the most common use for class attributes (other than 
methods, of course) is for default values which are over-ridden at the 
instance level:

class Page(object):
    unit = 'cm'
    width = 20.9
    height = 29.7
    direction = 'portrait'

p1 = Page()
p2 = Page()
p2.direction = 'landscape'


it would be disastrous to have every Page instance change to landscape 
just because you changed one, and it would be inconvenient to need 
special syntax to allow writing to the instance attribute just because a 
class attribute exists. The existing behaviour is, in my opinion, ideal.



-- 
Steven



More information about the Python-list mailing list