Class Variable Access and Assignment

Bengt Richter bokr at oz.net
Sun Nov 6 09:40:00 CET 2005


On Sun, 06 Nov 2005 15:17:18 +1100, Steven D'Aprano <steve at REMOVETHIScyber.com.au> wrote:

>On Sat, 05 Nov 2005 18:14:03 -0800, Paul Rubin wrote:
>
>>> instance.attribute sometimes reading from the class attribute is a feature
>>> of inheritance; instance.attribute always writing to the instance is a
>>> feature of OOP; instance.attribute sometimes writing to the instance and
>>> sometimes writing to the class would be, in my opinion, not just a wart
>>> but a full-blown misfeature.
>> 
>> But that is what you're advocating: x.y+=1 writes to the instance or
>> the class depending on whether x.y is mutable or not.
>
>Scenario 1:
>
>Pre-conditions: class.a exists; instance.a exists.
>Post-conditions: class.a unchanged; instance.a modified.
>
>I give that a big thumbs up, expected and proper behaviour.
>
>Scenario 2:
>
>Pre-conditions: class.a exists and is immutable; instance.a does not
>exist.
>Post-conditions: class.a unchanged; instance.a exists.
>
>Again, expected and proper behaviour.
>
>(Note: this is the scenario that Antoon's proposed behaviour would change
>to class.a modified; instance.a does not exist.)
>
>Scenario 3:
>
>Pre-conditions: class.a exists and is mutable; instance.a exists.
>Post-conditions: class.a unchanged; instance.a is modified.
>
>Again, expected and proper behaviour.
>
>Scenario 4:
>
>Pre-conditions: class.a exists and is mutable; instance.a does
>not exist.
>Post-conditions: class.a modified; instance.a does not exist.
>
Are you saying the above is what happens or what should happen or not happen?
It's not what happens. Post-conditions are that class.a is modified AND
instance.a gets a _separate_ reference to the same result. Note:

 Python 2.4b1 (#56, Nov  3 2004, 01:47:27)
 [GCC 3.2.3 (mingw special 20030504-1)] on win32
 Type "help", "copyright", "credits" or "license" for more information.
 >>> class A(object):
 ...     a = []
 ...
 >>> b=A()
 >>> id(A.__dict__['a'])
 49230700
 >>> b.a += [123]
 >>> id(A.__dict__['a'])
 49230700
 >>> id(b.__dict__['a'])
 49230700
 >>> (b.__dict__['a'])
 [123]
 >>> (A.__dict__['a'])
 [123]

Let's eliminate the inheritable class variable A.a:

 >>> del A.a
 >>> b.a
 [123]
 >>> id(b.__dict__['a'])
 49230700
 >>> vars(b)
 {'a': [123]}

Make sure we did eliminate A.a
 >>> vars(A)
 <dictproxy object at 0x02E817AC>
 >>> vars(A).keys()
 ['__dict__', '__module__', '__weakref__', '__doc__']

Is that the "wart" you were thinking of, or are you actually happier? ;-)

>Well, that is a wart. It is the same wart, and for the same reasons, as
>the behaviour of:
>
>def function(value=[]):
>    value.append(None)
IMO that's not a wart at all, that's a direct design decision, and it's
different from the dual referencing that happens in Scenario 4.

>
>I can live with that. It is a familiar wart, and keeps inheritance of
>attributes working the right way. And who knows? If your attributes are
>mutable, AND you want Antoon's behaviour, then you get it for free just by
>using b.a += 1 instead of b.a = b.a + 1.
Not quite, because there is no way to avoid the binding of the __iadd__
return value to b.a by effective setattr (unless you make type(b).a
a descriptor that intercepts the attempt -- see another post for example).

Regards,
Bengt Richter



More information about the Python-list mailing list