4

In Python, if you have a classvar, it's accessible from an instance, but if you set the variable on the instance it doesn't actually change the classvar, rather it assigns a new name which shadows the parent value:

>>> class Foo:
...     classvar = 10
...
>>> f = Foo()
>>> (Foo.classvar, f.classvar)
(10, 10)
>>> f.classvar = 30
>>> (Foo.classvar, f.classvar)
(10, 30)
>>> Foo.classvar = 9
>>> (Foo.classvar, f.classvar, Foo().classvar)
(9, 30, 9)

This is exactly akin to prototype inheritance in JavaScript:

> var proto = {x: 10};
> function Bar() { }
> Bar.prototype = proto;
> var b = new Bar(); 
> [proto.x, b.x]
[10, 10]
> b.x = 30
> [proto.x, b.x]
[10, 30]
> proto.x = 9
> [proto.x, b.x, (new Bar()).x]
[9, 30, 9]

Of course, if the child mutates the value, then it is seen in the parent, because the variable was not re-assigned and so no shadowing occurred:

>>> class Foo:
...     classvar = [10]
...
>>> f = Foo(); f.classvar
[10]
>>> f.classvar[0] = 30
>>> (Foo.classvar, f.classvar)
([30], [30])

My question is: Is there any good reason for this? It seems like it would be less of a "gotcha" if the assign semantics were "assign on any parent if the value exists there, otherwise create the new value".

I ask because I'm designing my own language and, as I get to choose what the semantics are, I'm wondering if I should break from the herd and do what seems like less of a gotcha.

2 Answers 2

4

Because if mutating the instance mutated the 'parent' there would be no point in having instances. The point of an instance is it has it's own memory. It's free to be different. The 'parent' just gives it default starting values.

If the 'parent' and the instance both point to the same thing though then they are sharing the memory of that thing so it should be no surprise that changes from one impact the other.

1

Eventually I concluded thusly:

If setting on a child has the behavior of "set on parent if parent has, otherwise set on self", then it is impossible for a child not to override a parent value. It also makes assignment not straightforward at all.

However, if the behavior is as it is normally, then assignment is really simple - always assign on self. In the case where you want to shadow a parent name, you can. In the case mentioned in the question where you want to mutate an immutable, then you have to do a work-around by wrapping the immutable in a container. So you have a work-around for everything you want, whereas the other approach limits you, is non-standard, and has less straightforward semantics.

1
  • 1
    Also, you could make a descriptor, where the setter does Foo.classvar = value, in that case you would get the behaviour OP wants. But having it the default would be prioritising a special case over the common case.
    – Jasmijn
    Commented Dec 20, 2016 at 11:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.