Treat underscores specially in argument lists
Often, underscores are used in function argument lists: def f(_): pass However, sometimes you want to ignore more than one argument, in which case this doesn't work: ryan@DevPC-LX:~$ python3 Python 3.4.1 |Anaconda 2.1.0 (64-bit)| (default, Sep 10 2014, 17:10:18) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux Type "help", "copyright", "credits" or "license" for more information.
def f(_, _): pass ... File "<stdin>", line 1 SyntaxError: duplicate argument '_' in function definition
My idea is to allow duplication of the underscore argument in the presumption that no one in their right (or wrong) mind is going to use it as a real argument, and, if someone does, they need to be slapped on the head. -- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated." Personal reality distortion fields are immune to contradictory evidence. - srean Check out my website: http://kirbyfan64.github.io/
On Mon, Feb 16, 2015 at 07:23:30PM -0600, Ryan Gonzalez wrote:
Often, underscores are used in function argument lists:
def f(_): pass
However, sometimes you want to ignore more than one argument, in which case this doesn't work:
Why are you ignoring *any* arguments? -- Steve
It's usually in callbacks where you only care about a few arguments. On Mon, Feb 16, 2015 at 8:41 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Feb 16, 2015 at 07:23:30PM -0600, Ryan Gonzalez wrote:
Often, underscores are used in function argument lists:
def f(_): pass
However, sometimes you want to ignore more than one argument, in which case this doesn't work:
Why are you ignoring *any* arguments?
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated." Personal reality distortion fields are immune to contradictory evidence. - srean Check out my website: http://kirbyfan64.github.io/
On Feb 16, 2015, at 18:45, Ryan Gonzalez <rymg19@gmail.com> wrote:
It's usually in callbacks where you only care about a few arguments.
A perfect example is using Tk variable tracing in tkinter (for validation or post-change triggering on widgets). The signature of the trace callback is callback(name, index, mode). You almost never need the index, you rarely need the mode, so you write: def callback(self, name, _, mode): In fact, you usually don't need the mode either (in fact, you often don't even need the name), but you can't write this: def callback(self, name, _, _): So instead you usually write something like: def callback(self, name, *_): Personally, I find *_ ugly, so I use *dummy instead. But whatever. So anyway, what if you needed the index, but not the name or mode? You can't use *_ to get around that; you have to come up with two dummy names. That's a bit annoying. The question is, how often do you really need to ignore multiple parameters, but not all of them (or all of them from this point on)? I can't remember any callback API that I've used where that came up, but I can imagine someone else has one:
On Mon, Feb 16, 2015 at 8:41 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Feb 16, 2015 at 07:23:30PM -0600, Ryan Gonzalez wrote:
Often, underscores are used in function argument lists:
def f(_): pass
However, sometimes you want to ignore more than one argument, in which case this doesn't work:
Why are you ignoring *any* arguments?
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated." Personal reality distortion fields are immune to contradictory evidence. - srean Check out my website: http://kirbyfan64.github.io/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Andrew Barnert writes:
So anyway, what if you needed the index, but not the name or mode? You can't use *_ to get around that; you have to come up with two dummy names. That's a bit annoying.
If there's one such function, I'd say "suck it up". If there are two or more, hit it with a two-by-four and it will submit: def twobyfour (func): def wrapper(real1, dummy1, real2, dummy2): return func(real1, real2) return wrapper
Why are you ignoring *any* arguments?
I wouldn't express the idea that way. Obviously we're ignoring arguments because there's a caller that supplies them. Rather, I'd say "in my experience ignoring arguments means a badly designed API that should be refactored." That's true in callbacks as well as in any other function. Granted, sometimes you can't refactor, but I think more often than not ignoring arguments is a code smell and you can.
Our experiences differ, then. I've never seen arguments unused that wasn't fairly justifiable and OK with me. OTOH, I *have* seen code break because it renamed a parameter to "unused_foo" instead of foo, and when the method was called with foo=2... kablooie. -1 on encouraging the antipattern of renaming your public interfaces to indicate implementation details. -- Devin On Mon, Feb 16, 2015 at 11:58 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
Andrew Barnert writes:
So anyway, what if you needed the index, but not the name or mode? You can't use *_ to get around that; you have to come up with two dummy names. That's a bit annoying.
If there's one such function, I'd say "suck it up". If there are two or more, hit it with a two-by-four and it will submit:
def twobyfour (func): def wrapper(real1, dummy1, real2, dummy2): return func(real1, real2) return wrapper
Why are you ignoring *any* arguments?
I wouldn't express the idea that way. Obviously we're ignoring arguments because there's a caller that supplies them.
Rather, I'd say "in my experience ignoring arguments means a badly designed API that should be refactored." That's true in callbacks as well as in any other function. Granted, sometimes you can't refactor, but I think more often than not ignoring arguments is a code smell and you can.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Feb 16, 2015, at 23:58, "Stephen J. Turnbull" <stephen@xemacs.org> wrote:
Andrew Barnert writes:
So anyway, what if you needed the index, but not the name or mode? You can't use *_ to get around that; you have to come up with two dummy names. That's a bit annoying.
If there's one such function, I'd say "suck it up". If there are two or more, hit it with a two-by-four and it will submit:
def twobyfour (func): def wrapper(real1, dummy1, real2, dummy2): return func(real1, real2) return wrapper
Why are you ignoring *any* arguments?
I wouldn't express the idea that way. Obviously we're ignoring arguments because there's a caller that supplies them.
Rather, I'd say "in my experience ignoring arguments means a badly designed API that should be refactored." That's true in callbacks as well as in any other function. Granted, sometimes you can't refactor, but I think more often than not ignoring arguments is a code smell and you can.
Sure, the tkinter var tracing callback interface is not very Pythonic--but that's because it's a thin wrapper around a Tcl interface. And keeping it thin means that when tkinter doesn't document everything, you can go real the Tcl/Tk docs, so I don't think you'd want to change that. (In fact, years ago, I actually did write a higher-level validation framework for Tkinter apps to deal with the haphazard variety or bindable events, validate functions, and tracing functions that different widgets provide in a more consistent and Pythonic way. I found it to be a bit easier to read, but a whole lot harder to debug, so I stopped using it.) And the same goes for C callback functions, ObjC blocks, JS DOM callbacks, PyCOM event handlers, etc. Or even large legacy Python frameworks. You pick the best framework for the job, and if it has parts of its interface that aren't to your liking, sometimes you have to deal with that.
On Mon, Feb 16, 2015 at 08:24:52PM -0800, Andrew Barnert wrote:
On Feb 16, 2015, at 18:45, Ryan Gonzalez <rymg19@gmail.com> wrote:
It's usually in callbacks where you only care about a few arguments.
A perfect example is using Tk variable tracing in tkinter (for validation or post-change triggering on widgets). The signature of the trace callback is callback(name, index, mode). You almost never need the index, you rarely need the mode, so you write:
def callback(self, name, _, mode):
In fact, you usually don't need the mode either (in fact, you often don't even need the name), but you can't write this:
def callback(self, name, _, _):
So instead you usually write something like:
def callback(self, name, *_):
Do I? I'm pretty sure I don't. I'm too worried about future maintainers, who will probably be violent psychopaths who know where I live. If the callback has signature name, index, mode, I write name, index, mode, and make it clear that the callback function is expected to receive exactly three arguments, and what they are. That way, I don't have to care whether the caller passes those arguments by position or name. I don't have to change the signature of my callback if I decide in the future that I actually do want to use the index and the mode. If I look at the arguments in a debugger, I'll see sensible names for all three parameters. If I use some sort of extended traceback (e.g. the cgitb module), I'll see exactly what the parameters were. And if something goes wrong, I won't have to worry about future maintainers tracking me back to my home. -- Steve
On Tue, Feb 17, 2015 at 5:24 AM, Andrew Barnert < abarnert@yahoo.com.dmarc.invalid> wrote:
On Feb 16, 2015, at 18:45, Ryan Gonzalez <rymg19@gmail.com> wrote:
It's usually in callbacks where you only care about a few arguments.
A perfect example is using Tk variable tracing in tkinter (for validation or post-change triggering on widgets). The signature of the trace callback is callback(name, index, mode). You almost never need the index, you rarely need the mode, so you write:
def callback(self, name, _, mode):
In fact, you usually don't need the mode either (in fact, you often don't even need the name), but you can't write this:
def callback(self, name, _, _):
So instead you usually write something like:
def callback(self, name, *_):
Personally, I find *_ ugly, so I use *dummy instead. But whatever.
So anyway, what if you needed the index, but not the name or mode? You can't use *_ to get around that; you have to come up with two dummy names. That's a bit annoying.
The question is, how often do you really need to ignore multiple parameters, but not all of them (or all of them from this point on)? I can't remember any callback API that I've used where that came up
Me neither. I remember doing this: a, _, _, _, b, c = foo ...but never wanting to do a similar thing applied to a function signature. A function strikes me as something "more important" than "a, _, _, _, b, c = foo" appearing in the middle of the code, something which mandates explicitness, even when some args are ignored, in fact I don't remember ever seeing "def callback(self, name, _)". -1 -- Giampaolo - http://grodola.blogspot.com
participants (6)
-
Andrew Barnert
-
Devin Jeanpierre
-
Giampaolo Rodola'
-
Ryan Gonzalez
-
Stephen J. Turnbull
-
Steven D'Aprano