[Python-Dev] defaultdict proposal round three

Alex Martelli aleaxit at gmail.com
Tue Feb 21 01:55:34 CET 2006


On Feb 20, 2006, at 3:04 PM, Brett Cannon wrote:
    ...
>>   - "Yes and it should be the only constructor argument."  This is my
    ...
> While #3 is my preferred solution as well, it does pose a Liskov
> violation if this is a direct dict subclass instead of storing a dict

How so?  Liskov's principle is (in her own words):

If for each object o1 of type S there is an object o2 of type T such  
that for all programs P defined in terms of T, the behavior of P is  
unchanged when o1 is substituted for o2 then S is a subtype of T.

How can this ever be broken by the mere presence of incompatible  
signatures for T's and S's ctors?

I believe the principle, as stated above, was imperfectly stated, btw  
(it WAS preceded by "something like the following substitution  
property", indicating that Liskov was groping towards a good  
formulation), but that's an aside -- the point is that the principle  
is about substitution of _objects_, i.e., _instances_ of the types S  
and T, not about substitution of the _types_ themselves for each  
other. Instances exist and are supposed to satisfy their invariants  
_after_ ctors are done executing; ctor's signatures don't matter.

In Python, of course, you _could_ call type(o2)(...) and possibly get  
different behavior if that was changed into type(o1)(...) -- the  
curse of powerful introspection;-).  But then, isn't it trivial to  
obtain cases in which the behavior is NOT unchanged?  If it was  
always unchanged, what would be the point of ever subclassing?-)  Say  
that o2 is an int and o1 is a bool -- just a "print o2" already  
breaks the principle as stated (it's harder to get a simpler P than  
this...).

Unless you have explicitly documented invariants (such as "any 'print  
o' must emit 1+ digits followed by a newline" for integers), you  
cannot say that some alleged subclass is breaking Liskov's property,  
in general. Mere "change of behavior" in the most general case cannot  
qualify, if method overriding is to be any use; such change IS  
traditionally allowed as long as preconditions are looser and  
postconditions are stricter; and I believe than in any real-world  
subclassing, with sufficient introspection you'll always find a  
violation E.g., a subtype IS allowed to add methods, by Liskov's  
specific example; but then, len(dir(o1)) cannot fail to be a higher  
number than len(dir(o2)), from which you can easily construct a P  
which "changes behavior" for any definition you care to choose.   
E.g., pick constant N as the len(dir(...)) for instances of type T,  
and say that M>N is the len(dir(...)) for instances of S.  Well,  
then, math.sqrt(N-len(dir(o2))) is well defined -- but change o2 into  
o1, and since N-M is <0, you'll get an exception.

If you can give an introspection-free example showing how Liskov  
substitution would be broken by a mere change to incompatible  
signature in the ctor, I'll be grateful; but I don't think it can be  
done.


Alex



More information about the Python-Dev mailing list