Class Variable Access and Assignment

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sat Nov 5 23:17:18 EST 2005


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.

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)

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.


> Say you have an
> immutable class with a mutable subclass or vice versa.  You'd like to
> be able to replace a class instance with a subclass instance and not
> have the behavior change (Liskov substitution principle), etc.

That's easy. You just have to make sure that the subclass implements
__iadd__ the same way that the immutable parent class does. 

You can't expect a class that performs += in place to act the
same as a class that doesn't perform += in place. Excessive data
abstraction, remember? 

L = list("Liskov substitution principle")
L.sort()  # sorts in place
print L   # prints the sorted list

class immutable_list(list):
    # __init__ not shown, but does the right thing
    def sort(self):
        tmp = list(self)
        tmp.sort()
        return immutable_list(tmp)

L = immutable_list("Liskov substitution principle")
L.sort()  # throws the sorted list away
print L   # prints the unsorted list

The only way the Liskov substitution principle works is if everything
works the same way, which means that all subclasses, all *possible*
subclasses, must have no more functionality than the subclass that does
the absolute least. Since the least is nothing, well, you work it out.

-- 
Steven.




More information about the Python-list mailing list