On Thu, Oct 31, 2019 at 02:47:35PM +0100, Andrew Barnert wrote:
> On Oct 31, 2019, at 13:56, Steven D'Aprano <[email protected]> wrote:
> >
> > I disagree. I think it's a pretty small breaking change,
>
> From my test earlier on the thread, using a function to raise makes a
> function that does nothing but raise and handle an exception take more
> than twice as long. Which implies that the actual raise part (not
> counting the outer function call, handling the exception, and
> returning) is far more than twice as long.
I don't recall seeing your test code, but I don't follow your reasoning
here. Unless you are using a custom Python build where the raise
statement has been replaced by a raise function, I don't see how you can
conclude that the raise statement inside a function takes "far more than
twice as long" as a bare raise statement.
Making raise a function will add the overhead of a function call, which
by my tests is smaller than the cost of a raise and catch:
$ ./python -m timeit "try: raise ValueError
> except ValueError: pass"
100000 loops, best of 5: 3.26 usec per loop
$ ./python -m timeit -s "def throw(): raise ValueError" "try: throw()
> except ValueError: pass"
50000 loops, best of 5: 5.44 usec per loop
YMMV.
So on my machine, raise-in-a-function is about 70% slower than raise,
not 100% or more. That's a pure-Python function. It's possible that a
builtin function in C will have less overhead.
But to paraphrase the prevailing opinion expressed in the "word list"
thread, you cannot possibly be seriously worried about a few micro-
seconds difference. (Only slightly serious.)
In this case, I'm not that worried about micro-benchmarks unless they
can be shown to add up to a significant performance degradation in
macro-benchmarks too. The cost of catching exceptions is, as I
understand it, quite a bit more expensive than actually throwing them.
We can't throw lots of exceptions without a corresponding catch, so I
expect that any additional overhead from a function call will probably
be lost in the noise of the catch.
But if that's not the case, then "performance" may end up being a good
reason to reject this proposal.
> Also, there are many places in the Python data model where you have to
> raise an exception, such as StopIteration;
Does the Python data model not have function calls? *wink*
> And what’s the advantage?
>
> If we need raise inside an expression without extra overhead, this
> doesn’t solve the problem; we’d need a raise expression (similar to
> the yield expression), not a raise function.
Yield expressions are a red herring here. Yield expressions are a weird
syntactic form that allow you to feed data into a running generator.
raise doesn't need to do that. The raise function will never return, so
it doesn't need to return a value. It will be kind of like os._exit, and
sys.exit.
> If we don’t care about the overhead, there is no problem to solve; you
> can already get this behavior with a one-liner. If that’s still too
> inconvenient or undiscoverable, we can add the one-liner to builtins
> without removing the statement at the same time. So, what’s the
> benefit of removing the statement?
"Only One Way To Do It" *wink*
We could have added a print_ function and kept the print statement too.
And for years, people wrote their own one-liner print_ functions. (I
have lost count of the number of times I did.) If your argument was
compelling, we never would have changed print and exec to functions.
But we did. The "just use a one-liner" argument isn't very strong. It's
a work-around for the missing functionality, not a positive argument in
favour of the status quo.
If, back in the early days of Python, Guido had made raise a function,
we'd all accept it as normal and desirable. I doubt anyone would say "I
really wish it was impossible to call raise from an expression without
having to write an unnecessary boilerplate wrapper function".
As I said above, I'll grant you the *possibility* that performance may
tilt the balance, but honestly, I see no other reason why we should
prefer a *less flexible, less useful* raise statement over a more
flexible, more useful function. Especially if a built-in function
written in C can avoid most of the pure-Python function call overhead,
narrowing the performance gap.
--
Steven
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/AJTPDZTODJW34VYCRDEXHUMSG4BYMCQ2/
Code of Conduct: http://python.org/psf/codeofconduct/