Style question - defining immutable class data members

R. David Murray rdmurray at bitdance.com
Sun Mar 15 22:20:12 EDT 2009


John Posner <jjposner at snet.net> wrote:
> Summary: I no longer suspect that "Python is broken". I *do* think that
> there's a situation that is potentially quite confusing:
> 
>  * In the statement "self.x = self.x + 1", the two "self.x" names can
>  sometimes refer to different objects.

But this is fundamental to Python.  The name (or slot, in the case of say
a slice assignment) on the left hand side is being given a new binding
by the assignment statement.  So whatever object the 'self.x' on the
left hand side may have pointed to before the statement is executed is
irrelevant (if we ignore '+=' and kin).  That is the essence of assignment
in Python.  If you are surprised by this, then you should probably study
up a bit on the way namespaces work in Python.

What I think you meant is that even though both are represented by the
same token sequence in the source ('self.x'), the two 'x's are actually
located in two different namespaces.  The one on the left hand side of
the assignment is local to the instance, while the one on the right hand
side can cascade upward to the class namespace and resolve to an object
from there.

>  * Even worse, in the equivalent statement "self.x += 1", the single name
>  "self.x" can sometimes refer to two different objects!

Now on this one I'll agree with you about the "worse" part.  Consider:

    >>> class A(object):
    ...     x = [1]
    ...     def f(self):
    ...             self.x = self.x + [2]
    ...             print self.x
    ... 
    >>> 
    >>> m = A()
    >>> m.f()
    [1, 2]
    >>> A.x
    [1]
    >>> class B(object):
    ...     x = [1]
    ...     def g(self):
    ...             self.x += [2]
    ...             print self.x
    ... 
    >>> n = B()
    >>> n.g()
    [1, 2]
    >>> B.x
    [1, 2]

I'm inclined to call that a problem, myself, even though I understand
why it happens (+= mutates the object pointed to by the class variable
'x', and _then_ the instance variable 'x' is created and pointed at the
same object.  Whereas in the '+' case, a new list is created and the new
'x' instance variable is pointed at that new list.)

There is a somewhat analogous situation with variables in the local scope of
a function and global variables in the module.   For example, we might have:

    >>> x = 1
    >>> def f():
    ...     print x
    ... 
    >>> f()
    1

So, when 'x' isn't found locally, the value gets picked up from the global
namespace.  The difference from the class/instance case is when we try
to assign to it:

    >>> def g():
    ...     x = x + 1
    ... 
    >>> g()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in g
    UnboundLocalError: local variable 'x' referenced before assignment
    >>> x = []
    >>> def g():
    ...     x += [1]
    ... 
    >>> g()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in g
    UnboundLocalError: local variable 'x' referenced before assignment

So in this case Python is warning us about the namespace difference.

Why are the two cases handled differently?  To tell you the truth, I'm
not sure.  I've been programming in Python for years and it just seems
to make sense to me to do it that way.  It does allow you to use class
variables as default values for instance variables, as long as you are
careful when using mutable objects.  But you could argue that it should
behave in a way analogous to local/global.  It would be interesting to
see that argument laid out in full, with all the consequences it would
entail examined.

> I think this situation should be handled in documentation. (I'm a tech writer
> in my day job ... oh wait, I forgot ... I got laid off from my day job in
> December.) I'll look into what the standard Python doc set says on this
> matter.

Doc patches are always welcome, and from what I hear easier to get
accepted than code patches ;)

--
R. David Murray           http://www.bitdance.com





More information about the Python-list mailing list