On Wed, 04 Nov 2009 02:29:21 +0100, Alf P. Steinbach wrote: >>> For example, consider two rectangle classes R1 and R2, where R2 might >>> be a successor to R1, at some point in system evolution replacing R1. >>> R1 has logical data members left, top, width and height, and R2 has >>> logical data members left, top, right and bottom. With R1 direct >>> changes of left and top keeps the rectangle's size (since that size is >>> specified by width and height), while with R2 it changes the >>> rectangle's size. R1 is implemented with logical data members as >>> directly exposed data attributes. Some code in the system deals only >>> with R1 objects and for convenience or efficiency or whatever uses >>> direct modification instead of set_position method. Due to new >>> requirements it instead has to deal with R2 objects, with same >>> methods. But due to the direct modification of object state it now >>> changes the rectangle sizes, but at first it's not noticed since the >>> attempted rectangle position changes are very small. People get upset. >>> The bug is fixed. Time has been wasted. >> >> If there is need for mutable rectangles, there is need for mutable >> rectangles. Using properties instead of attributes doesn't help > > In the example above using properties would have avoided the problem.
How would it have avoided the problem? Either of these would have the exact same semantics: class R2(object): def __init__(self): self._secret = {'top': 0, 'bottom': 100} def _top_getter(self): return self._secret['top'] def _top_setter(self, value): self._secret['top'] = value top = property(_top_getter, _top_setter) vs class R2(object): def __init__(self): self.top = 0 self.bottom = 100 Given the semantics you specified, it is strictly irrelevant whether R2.top is an attribute or a property. That's an implementation detail. Now of course we're capable of imagining a rectangle class R3 where R3.top is a property which has the side-effect of also changing R3.bottom so as to avoid the resize. Great -- that's something you can't implement (easily, if at all) with bare attributes. But that class is not R2, it has different semantics to R2. We're not opposed to properties where the functional requirements are best suited by computed properties. But properties don't come for free, and if you're going to implement functional behaviour *identical* to bare attributes using properties, then what's the point? >> - if you >> change semantics of something, code might break. > > Yes, but that's totally out of the blue. If you, say, paint your nose > bright green, then people might stare at it. That has nothing to do do > with anything discussed here, and so anyone mentioning that as > purportedly relevant to anything discussed here, would implicitly be > saying "I don't understand anything abour it", and that's effectively > what you're saying, sorry. I'm afraid you have failed to understand Diez's point. The hypothetical project using class R1 had a specified functional behaviour: assigning to rectangle.top must move the rectangle, not resize it. You have replaced it with a class that has different behaviour. Who cares what the implementation is? It's the *change in behaviour* that caused the breakage, regardless of whether R2.top is implemented as a bare attribute or as a property. In that regard, it is *no different* from changing R2.set_position() to resize the rectangle. > However, I do understand what got you confused. I doubt that very much. > Changing the internal representation of a class is not to change the > class' semantics. That's true, but that's not what you've done in your example. You've clearly changes the class' semantics. You said so yourself: "With R1 direct changes of left and top keeps the rectangle's size (since that size is specified by width and height), while with R2 it changes the rectangle's size." Since in Python, non-underscore attributes are part of the public API, the two classes have different semantics. > It belongs to the class only. Anyone availing himself > or herself of access to that internal representation is doing something > that may or may not work in the future and would ideally be doing it at > own's risk, but as the example above illustrated, they're most often > doing it at *other*'s risk. Irrelevant. In Python, attributes are frequently part of the class API. If you have an attribute R1.top, then people will assign to it, and your class should deal with it. "Deal with it", by the way, may include saying "if you stuff something crazy in the attribute, then you are responsible for breaking it". Python classes tend to be written under the implicit promise/threat that if you do something stupid, you're responsible for the breakage. That allows the caller the responsibility to do something clever which you never thought of without having to defeat your defensive code, but with great power (duck-typing) comes great responsibility (don't set R1.top to None unless you want to see some amusing exceptions). > And so the issue is how to get them to not do it, even when they think > that nobody will check their code until next version of Lib XYZ comes in > a year... > > And that's where using properties from the start enters the picture, > making it less convenient for those tinkerers to use internal > representation details. This is Python. Introspection is easy, and messing up your internals is easy unless you write your class in C. Just because you hide something behind a property doesn't mean we can't find it and do strange and terrible things to it. We don't do it because when we do, it's our own foot we're shooting. >> In Python, attributes >> are part of the public interface as long as you don't explicitly define >> them otherwise. > > Not sure what that refers to. Dear me. Here you are talking about "internals", and you don't see the connection with the public interface? Hint: if something is in the public interface, it isn't an internal detail. >> But preliminary assumptions about what could be in some yet unseen >> future is introducing more code with more chances of errors > > No not really. Of course it is. Properties are *code*. Every line of code can contain bugs. Properties require testing. They don't fall from the sky and insert themselves into your class, you have to write them, test them, debug them, maintain them. >>, reducing >> the flexibility at the same time. I still fail to see where that's >> good. > > Yes. > > Consider the *nix convention for files, that they're just byte streams. > They're *restricted* to byte streams. That's darn inflexible, yes? No. Anything can be represented by a byte stream with a known encoding, provided you are willing to deal with errors. -- Steven -- http://mail.python.org/mailman/listinfo/python-list