Tkinter callback arguments

Steven D'Aprano steven at REMOVE.THIS.cybersource.com.au
Wed Nov 4 06:35:52 CET 2009


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



More information about the Python-list mailing list