En Wed, 04 Nov 2009 03:15:14 -0300, Alf P. Steinbach <al...@start.no> escribió:
* Steven D'Aprano:
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

OK, I had a laugh. :-) You maintain that all doors are dark red, and show up a photo of two of your dark red doors as proof.

For R2, did you at *any* moment consider properties that emulated the internal representation of R1?

I'm putting it that way on the off-chance that you're not just pretending to not understand.

I don't understand either. R1 and R2 have *different* semantics. They don't behave the same. Any breakage you experiment using R2 instead of R1 comes from the fact they behave differently, not because they're implemented differently, nor because they use properties or not. You could have implemented another variant, let's say RCrazy, that behaves exactly the same as R1 but internally stores a different set of attributes (the baricenter, the angle between both diagonals, and the diagonal length). As long as you implement the same public interfase (same set of externally visible attributes, same methods, same behavior) RCrazy is interchangeable with R1; RCrazy is a subtype of R1 in the sense of the Liskov substitution principle. Of course, perfect substitutability (did I spell it right?) isn't possible in Python; obj.__class__.__name__ returns 'RCrazy' instead of 'R1', it's mro() is different, dir() returns a different set of names, etc. But for any `reasonable` use of an R1 instance representing a rectangle, an RCrazy instance should serve equally well.

Given the semantics you specified

No semantics was specified in my example, quoted in full above.

However, the natural semantics is that various logical properties, such as left, top, right, bottom, width and height, can be varied independently.

You did specify the set of attributes and how they behave, perhaps not very formally, but that's a semantic specification to me. It was clear from your description that R2 behave different that R1; the problems that came after using R2 instead of R1 were caused by such different behavior, not because of using properties or not. In any case, I don't think the problem is specific to Python.

it is strictly irrelevant whether R2.top is an attribute or a property. That's an implementation detail.

Happily that's incorrect. You might try to consider what properties are *for*, why the language supports them if they do nothing at all except adding overhead.

In normal usage (either obj.name or getattr(obj, 'name')) an attribute is undistinguishable from a property. Users of the class should not worry at all whether it is implemented as a simple attribute, a computed property, or an esoteric class attribute following the descriptor protocol. If all a property does is to store and retrieve its value as an instance attribute, yes, it just adds overhead.

--
Gabriel Genellina

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to