Alf P. Steinbach wrote: > * Peter Otten: >> 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". > > Thanks! > > >>> 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 > > I think I get the gist that locally saving lines of code, and generally > avoiding having to write empty argument list parentheses, and thereby also > indicating in a way that one is accessing a logical data attribute, is > considered good in Python, which goes to initial development time and the > amount of code that one > must scan to grok it both for definition and usage -- is that correct?
Remember that .id does contain static data in your case. If it were a lengthy calculation the .id() method would be OK. > But the trade-off is inviting modification of a supposedly fixed id, which > goes to correctness and later fix-it time? > > >> You can always switch to >> >> class A: # assuming 3.x >> @property >> def id(self): >> id = arbitrary_code() >> return id >> >> later. > > Thanks, now I learned about @property... :-) > > But when the thing has been used it's much more difficult to restrict the > functionality (making read-only, breaking code that changes id) than to > add functionality (making also writeable, breaking none of existing code). > > So isn't "later" a bit late to make it read-only, shouldn't that be the > initial design, and then possibly adding a setter later if id's turn out > to not be so constant after all? You can break existing code with changes in a library in so many ways that I don't think this specific problem matters much. But if you don't feel comfortable with writing a warning into the documentation use a getter function or a property. >>> 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: ... > > Thanks! > > But why is this preferred? > > >>> 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" > > Thanks! > > *Note to myself*: check if there are more such conventions. > > >>> 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. > > I don't understand what you mean. Writing code is not fire and forget. It has to be debugged, tested, maintained, and will be read quite a few times in the process. Therefore it is important that you make it easy to read and understand. > Not that it's a shiny example of code that does a lot, but it (1) > simplifies and shortens creation of buttons with id, and (2) provides a > nice start for adding other customizations of those buttons, and (3) > supports searching for a button with given command id, e.g. for purpose of > hiding or enable/disable. > > If I didn't just want to try out writing a Python class, which I've never > done before so it appeared interesting, I'd probably just skipped points 2 > and 3 and written the same functionality as a factory function, like > > > <code> > import tkinter > > def id_button( owner_widget, id, command = None, **kwargs ): > def tk_command( an_id = id, a_command = command ): > if a_command is not None: a_command( id ) > return tkinter.Button( owner_widget, kwargs, command = tk_command ) > > def on_button_click( id ): > print( "Button " + str( id ) + " clicked!" ) > > window = tkinter.Tk() > > n_buttons = 3 > for x in range( 1, n_buttons + 1 ): > id_button( > window, id = x, text = "Button " + str( x ), command = > on_button_click ).pack() > > window.mainloop() > </code> I prefer that one, though without extra spaces in funny places. > but once you have the class, for whatever reason, it would be silly not to > use it, especially since it provides points 2 and 3 which the function > doesn't. > > By the way, I as yet know next to *nothing* about binding of variable > references within a function such as tk_command above. Probably I've done > Unnecessary Things(TM) above? Nothing too obvious; you could avoid the noop tk_command, and maybe provide the button instead of the id (all code untested): def make_button(owner, id, command=None, **kwargs): button = tkinter.Button(owner, kwargs) button.id = id if command is not None: button["command"] = functools.partial(command, button) button.pack() def button_click(button): print(button["text"], "clicked!") >>> 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*. > > See above, points 2 and 3. Most of that class has to do with 2, > customization ability. Couldn't class IdButton(tkinter.Button): def __init__(self, id, **kw): self.id = id tkinter.Button.__init__(self, **kw) be customised as easily? >>> 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)? > > Because the string representation of an id then /can/ be customized > independently of the id. For example, id's might be integers but for > string representation you might want symbolic action names (e.g., you > might have two or more buttons with same title but different actions, so > that title would be ungood to identify button). And for another example, > when debugging or testing you might want the string represention of an id > to provide more information about the button and/or its context, and then > id_string provides a single > central customization point -- provided it's used, of course. <g> And what about the IdEntry class? To me it would make more sense to customize the type of the .id value. >> The >> programmer will hesitate, wonder whether to use button.id() or >> button.id_string(), how the two may interconnect... > > Hm, see immediately above. > > It's perhaps a different way of thinking? > > >> It feels more like a hoop to jump through than a helpful service >> providing tried an tested code. > > Yes. > > It's the old "every computer science problem can be solved by adding an > extra layer of indirection". > > Sometimes it's nice when you can do that centrally. Retrofitting the > indirection to existing client code can be hard. Again, just-in-case indirections come at a cost. You are probably right about the way of thinking. >>> 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/ >> ? > > No. :-) But you're right that testing isn't that much of an issue for > that class. If that's what you meant. I read it twice but didn't understand it. You probably misplaced a word, but I can't figure out which one. >>> 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. > > The design is that for any given IdButton there's a single point of > responsibility for the click action, namely either a button creation code > supplies that action, or it relies on the action defined in the class. > > I.e. again customization ability, that the button creation code can > /override/ the class provided action, per button, without getting into > general overriding of class methods, just by defining a nice little lambda > inline in the call. > > But as I learn more Python I may perhaps find that overriding class > methods can > also be done that conveniently -- I don't know, keep in mind I'm a > Python newbie, and doing Very Much Else than studying Python. :-) > > >>> 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() >> > > Uh, could you expand on how that's redundant and how to make it less so? How about class IdButton(tkinter.Button): def __init__(self, owner, id, command=None, **kwargs): tkinter.Button.__init__( self, owner, kwargs, command=self.on_clicked) self.id = id self.specified_command = command def on_clicked(self): if self.specified_command is not None: self.specified_command(self) Peter -- http://mail.python.org/mailman/listinfo/python-list