Maybe what they're planning to do with this is allow wrappers to hide the places where exception handling is gratuitous, and therefore try to bring Python forward into the world of more modern languages.
The (result, error) pattern in Go or Result<Ok(res), Err(error)> pattern in Rust usually mixes the two. Unless you're doing and_then in Rust, but Go doesn't have anything like that. If anything, I feel like it's exceptions that separate the error handling for the conventional logic. You have your normal code in try, and your error handling in except.
This is a very odd claim. Go and Rust are extreme examples of mixing up error handling with conventional logic. There are excellent reasons for doing it that way, but the fact remains that they do. Exceptions, on the other hand, definitely do separate error handling from conventional logic – that's the whole point of them.
I think you just happen to have seen Python code bases where exceptions are caught very close to where they are being thrown, but that's property of the code you read, not the language feature. And presumably you have also seen Rust/Go code bases where errors are often passed back up the stack, which is easy to do but still requires some code (even just a ? in Rust is still an explicit decision) in a way that allowing exceptions to propagate up does not.
The sad thing is that there really isn’t any “learn elixir” book that teaches this idiomatic design. A student of Elixir should set up an umbrella application from the very first hello world, in my opinion.
item = some_dict.get(key)
if item is None:
# key does not exist
Versus try:
item = some_dict[key]
except KeyError:
# key does not exist
When I tested these (admittedly, a while ago), which one was faster depended on how often the key was missing. If “missing key” was an expected case, the first one was faster. If “missing key” was uncommon, the second was faster. It sounds like the fast path in the second case is getting faster, so this performance gap may be increasing.First approach is looking for `get` in `type(some_dict).__dict__` and then for `key` in `some_dict`. Second approach is looking for `key` in `some_dict`, and then (only if missing) for `KeyError` in the module globals/builtins.
If the performance of hash lookups matters, Python is the wrong language for you.
Announcement to Python programmers: “Don’t bother trying to improve the performance of your Python code! If performance matters, just completely rewrite your code in a different language!”
I don’t know how to respond to that, except to disagree with the underlying assumptions that (1) there is a “right language”, (2) if performance matters, Python is not a suitable language, or (3) people are generally in a position to choose which language a project is written in.
Even if performance matters, it is not the only thing that matters. When you choose a language, there are necessarily tradeoffs... everything from the skillset of your team, to the ecosystem of libraries available affects that decision. Finally, there are projects already written in Python.
Here, the number of steps directly affect the time.
In the first approach, the ".get()" method first analyses the type of "some_dict" and then uses an internal variable (the ones surrounded by double underscores) to try and fetch the value by using the provided key. If the key is present, then the value is returned, if not then a default value is returned. So if the key does not exist, the returning the default value saves 1 step (that of fetching the value from the map)
In the second approach, the exception raises the number of steps because the type of error has to be determined and the stack is traced every time an exception is raised. So the more exceptions are raised, the slower the code gets.
I tested this with 3.9.7 right now and in my testing, the runtime of first approach was virtually unchanged, while the second one was faster if exceptions were raised ~12% of the time or less. (I ran both 10 million times)