
From: Greg Ewing <greg.ewing@canterbury.ac.nz> Sent: Tuesday, June 25, 2013 3:09 PM
Andrew Barnert wrote:
What are the use cases you have in mind?
Here's a simplified version of something that crops up in various places in my GUI libraries. I have a widget, let's say a Button, and I want to calculate a default size for it during construction. To do that I need to know its font, which is one of the things that can potentially be passed as an argument to the constructor. So I want to "peek" at the font argument as it goes by:
class Button(Widget):
def __init__(self, text = "Hello", font = system_font, **kwds): default_size = calc_default_size(text, font) Widget.__init__(self, size = default_size, font = font, **kwds)
OK, I recognize this kind of thing. And I also recognize your uneasy feeling with it. Notice that in this case, you don't actually need to pull out font and pass it along: def __init__(self, text = "Hello", **kwds): default_size = calc_default_size(text, kwds.get('font', system_font)) Widget.__init__(self, size = default_size, **kwds) That also removes the need to copy the default value from the base class, and I think it gets across the idea of "peeking at the kwargs" more clearly than pulling one out into a positional-or-keyword arg. However, I'm not sure it's actually more readable this way. And it's certainly more verbose, not less, and all those get calls would add up if I were peeking at a lot of values. Also, that doesn't help if you want to do something with the value: def __init__(self, text = "Hello", font = system_font, **kwds): if os.sep in font: font = load_font_file(font) default_size = calc_default_size(text, font) Widget.__init__(self, size = default_size, font = font, **kwds)
Now, it's not so bad when there are only one or two passed-on arguments, but if you have a handful it starts to feel a bit wanky.
I think the issue isn't how many passed-on arguments you have, but how many you're peeking at. If you've got 20 arguments to pass along, but only need to peek at 1 or 2, your code is fine; it's when you've got 6 arguments to pass along and need to peek at all 6 that it gets nasty.
I think I've figured out why this seemingly-small amount of redundancy bothers me so much. It's not just that I feel I'm saying the same thing twice, but that I feel I'm not saying *anything at all*. If I were to write this as an ordinary assignment:
font = font
then it would be completely redundant. Now I know full well that it's different with a keyword argument, because the two sides live in different namespaces. But some part of my brain still tries to tell me that I'm doing something silly
Actually, I think it's a different problem for me. My brain initially wonders why I'm trying to use the default-value hack: filters = [lambda x, modulus=modulus: x % modulus == 0 for modulus in range(2, 6)] … on a function call instead of a function definition. Anyway, now that I see your example, I'm definitely more sympathetic to the problem. But I still don't like the proposed syntax, even if the problem were nearly compelling enough to add more syntax—which I don't think it is.