Tkinter callback arguments

Peter Otten __peter__ at web.de
Mon Nov 2 07:18:18 EST 2009


Alf P. Steinbach wrote:

> * Peter Otten:
>> Alf P. Steinbach wrote:
>> 
>>>>     for x in range(0,3):
>>>>         Button(......, command=lambda x=x: function(x))
>>> An alternative reusable alternative is to create a button-with-id class.
>>>
>>> This is my very first Python class so I'm guessing that there are all
>>> sorts of issues, in particular naming conventions.
>> 
>> Pseudo-private attributes
> 
> That means there is some way of making attributes private?

No, there isn't. And the name mangled __attribute is hardly ever needed. Use 
_attribute to convey the message "If you mess with this attribute you're on 
your own".
 
> Probably that comes across as an inane question but I ask it anyway. I
> haven't really started to look at Python classes. I'm guessing that by
> asking here I may learn things that are not obvious from the
> documentation.
> 
> 
>>, javaesque getter methods,
> 
> What do you mean by that?

In Java you have a private attribute and a public getter method. In Python 
you can just make the attribute public, i. e.

# bad
class A:
    def __init__(self):
        self._id = 42
   def id(self): return self._id

# good
class A:
    def __init__(self):
        self.id = 42
 
You can always switch to

class A: # assuming 3.x
    @property
    def id(self):
        id = arbitrary_code()
        return id

later.

> What I associate with Java getter method is mainly the "get" prefix, for
> Java introspection.
> 
> 
>> unidiomatic None-checks
> 
> What's the idiomatic Python way for an optional thing?

if some_value is None: ...

> In this case one alternative I see could be to get rid of the
> __on_tc_command method and more directly tell tkinter.Button to call the
> relevant function, doing the if-else choice once only in the IdButton
> constructor.
> 
> Is that what you mean?
> 
> I'm thinking more in terms of customization points when I write code.
> 
> So I tend to avoid hardcoding things internally in methods, instead
> designing the choices out to where they're accessible to client code.
> 
> 
>>, broken naming conventions (**args),
> 
> How to do this argument forwarding in modern style?

I meant that keyword args are traditionally named kw or kwargs,  the name 
"args" is normally used for positional arguments:

def f(*args, **kw):
    "whatever"

> Or is there an alternative to argument forwarding for this example?
> 
> 
>> spaces in funny places...
> 
> Bah. ;-)
> 
> 
>>> And the idea of creating a reusable solution for such a small issue may
>>> be un-pythonic?
>> 
>> Screw pythonic, the signal/noise ratio is awful in any language.
>>  
>>> But just as an example, in Python 3.x,
>> 
>> ...for achieving less in more lines?
> 
> Now, that's a good Python-independent question! :-)
> 
> Re your question's the number of lines: /if/ the code is heavily reused
> then the number of lines doesn't matter since they're only written /once/;

Every time someone has to read the code he will read, hesitate, read again, 
and then hopefully come to the conclusion that the code does nothing, 
consider not using it, or if it is not tied into a larger project removing 
it.

> the net effect can even be to reduce the total number of lines, or at
> least the number of apparent function points (or whatever size metric).
> That's part of what "reusable" means. For example, if the OP used the code
> then he or she didn't type them lines, but just copy/paste'd them, less
> work than typing in a lambda definition in every button creation, and more
> clear code at every creation.

But most of your code does *nothing*.
 
> Re your question's what (the) reusability achieves.
> 
> First, when you and others have used such a thing in a number of places
> then you gain confidence in correctness. For example, you don't wonder
> whether Python's str type is correct, and when you test your program you
> don't test the str type implementation. Since it's been used so much you
> know that it (mainly) is correct, and that any remaining bug in there
> can't be all that serious, because if it was then it would've surfaced in
> earlier use of the type.

The theory may be OK, but in practice it doesn't always work out. Example: 
Why do you introduce button.id_string() instead of str(button.id)? The 
programmer will hesitate, wonder whether to use button.id() or 
button.id_string(), how the two may interconnect... 
It feels more like a hoop to jump through than a helpful service providing 
tried an tested code.
 
> This advantage of confidence in correctness can be realized even without
> heavy reuse, because the encapsulation that's necessary for reuse, here
> having the code in a class, also makes it possible with more centralized
> testing.

Was this sentence/paragraph produced by http://pdos.csail.mit.edu/scigen/ ?

> A centralized, encapsulated piece of code can be tested to death and
> deemed correct (enough) and frozen, while the application of a code
> pattern in umpteen places in umpteen programs, generally can't.

I'd like to see a good test suite for your IdButton class, especially how it 
copes with the design decision that you can override the on_clicked() stub, 
or provide a command function, or both.

> Second, when you do find a bug, or need more functionality, or whatever,
> there's just /one place/ to fix/extend, whatever, instead of updating
> umpteen places in umpteen programs, and trying to be sure that you've
> covered all instances and done the right thing every place regardless of
> local variations (which is pretty hopeless in general, but my experience
> with that kind of badness has mostly been with C, not Python). More
> technically, it's reduced redundancy, in this case avoiding redundant
> application of a code pattern everywhere one needs a button with id (if
> that happens often). Reduced redundancy = good.

I agree with that maxim. Incidentally I have just found a nice example of 
redundancy for you:

>>>      def __on_tk_command( self ):
>>>          if self.__specified_command != None:
>>>              self.__specified_command( self )
>>>          else:
>>>              self.on_clicked()

Peter




More information about the Python-list mailing list