
On Mon, Dec 6, 2021 at 5:20 AM Christopher Barker <pythonchb@gmail.com> wrote:
On Sun, Dec 5, 2021 at 3:28 AM Chris Angelico <rosuav@gmail.com> wrote:
(That said, though: it would be rather nice to be able to do algebra with function signatures. For instance, you could say "my signature is that function's kwargs plus frobnosticate=42" or "my signature is that function's kwargs minus stdin". But that's a topic for another thread or another day.)
Heck, or even " my signature is that other function's signature" -- that is what passing *args, **kwargs does, but you have to look at the implementation to know.
Ah, if it's absolutely exactly "that other function's signature", then set func.__wrapped__ to the other function (that's what @functools.wraps does to make the signature work). But that's the only option. You can't do modifications in this way: def func(*args, **kwargs, frobnosticate=42): ... basefunc(**args, **kwargs) def func(*args, **kwargs): if "stdin" in kwargs: stdin = kwargs.pop("stdin") ... basefunc(*args, **kwargs) You have to do all-or-nothing at the moment. I don't know of a good way to make this happen, but if someone has a brilliant idea, I'd love to hear one.
As it happens, right now, someone on my team is trying out an implementation that uses inspect to grab the signature of superclass methods so that we can have a complete function signature without repeating ourselves all over the place. Not sure that's a good idea, but it would be cool if there were a standard and reliable way to do that.
If you mean that it's chasing all the way up the class hierarchy, building the signature piece by piece, then you're right, there's no easy way to do that at the moment. Signature algebra would allow you to do that - you'd have each function say "that function, but these changes" - but how you specify that is the hard part.
But yes, topic for another day.
Indeed.
None is most assuredly not going to trigger a late-bound default.
I don't think so, because None doesn't mean "omit this argument". It is a perfectly valid value. There's also no need to say that object() won't trigger late-bound defaults, or 0, or anything else. The only way to cause a default argument to be evaluated is to not pass the argument - as is already the case.
But I'd like to see as a (perhaps rejected) idea is to have a new sentinel that does mean undefined.Sure there could be (rare) cases where you would need to have it a valid value, but then maybe you can't use late-bound defaults in that case.
This is very different from None, because it would be new, so no one is already using it for anything else. And sure, folks could choose to use it inappropriately, but consenting adults and all that.
I think that would be more confusion than it's worth. Having an object with no value is a means of major insanity. I'll give a specific example of something that frustrated me from JavaScript; this kind of API is very common: // If "foo" is present, remove it. Otherwise, add it classList.toggle("foo") // Add "foo" classList.toggle("foo", 1) // Remove "foo" classList.toggle("foo", 0) These are defined using the standard truthiness rules: any true value will add, any false value will remove. And if you don't pass anything at all, it toggles. Next up, consider this: state = { "title": "some title", "is_open": true/false, "etc": etc, } // Check to see if the thing is open or not if (state.is_open) {...} else {...} Cool. Nice and easy. Both of these use the truthiness rules. Seems pretty normal, right? Unfortunately, there is ONE special value which doesn't behave the same way: undefined. In an 'if' statement, undefined is falsy, so you'd go into the 'else' clause. Just like None in Python, just like every language with a concept of truthiness/falsiness. But in the toggle() call, undefined is indistinguishable from not passing the argument at all. So instead of removing, it will toggle. That specific issue cost me some debugging time, because I didn't even think to ask the question "why is state.is_open undefined instead of false" - because in every other way, undefined did indeed behave as if it were false.
Yeah :) I say this because, in JavaScript, there is fundamentally no difference between passing the special value 'undefined' (kinda like None, although there's also null as a separate value) and not passing the argument at all, which means that...
function foo(x="hello") {console.log("x is " + x);} foo(undefined); foo(foo.any_unknown_attr);
will print "x is hello" twice. I don't want that :)
Sure, that's ugly, but isn't the real problem here that foo(foo.any_unknown_attr) doesn't raise an Exception? Would we have that same issue in Python?
Well, true, that particular part of it is handled by that. But in Python, you might get something from a dictionary using foo.get("any_unknown_key") and you'll still get back None. What's important is that in Python, instead of getting back a null value that behaves as if you didn't pass anything at all, you get back a value that is a real thing in and of itself. None isn't the absence of a value - it is a value that has real meaning. (And real attributes, though not many of them.)
e.g., doesn't Javascript already have:
foo(); foo(foo.any_unknown_attr);
lead to the same thing? whereas in Python, that would raise, yes?
Or is there something special about undefined that I'm missing? (sorry, I don't really "get" Javascript)
There are two halves to that second example: firstly, that foo.any_unknown_attr is undefined rather than being an error; and secondly, that passing undefined to a function is indistinguishable from not passing an argument. You can get undefined from all kinds of sources, and it's often used in places where Python would use None, so that part isn't a major problem. The problem is that, in a function call, it ceases to be a value, and becomes a non-value. (And don't worry. Nobody really "gets" JavaScript. We just use it anyway, since it's the thing that browsers are most comfortable with.)
I personally think more standard special purpose sentinels would be a good idea, though I understand the arguments made against that in previous discussions. But this is a little different, because late-bound defaults are a new thing, so we don't already have a body of code using None, or anything else for "use the late bound default".
Hmm, the problem with a multitude of standard special-purpose sentinels is that, inevitably, you need something that lets you enumerate all standard sentinels, and also say "nothing to see here". So you'll end up needing your own private sentinel. The standard ones don't end up buying you much, unless they have meaning to the language itself (as NotImplemented does). ChrisA