Python 3 is a mess. How did this happen?
Python3 is a mess. How did this happen? So many of the changes seem to me to fly in the face of the whole Zen of Python aesthetic. The two biggest offenders, in my opinion, are asyncio and type hints.
Armin Ronacher's excellent post I don't understand asyncio pretty well covers the garbage fire that is
twisted asyncio. It's a tangle of complexity that even GvR struggles (oh so painfully) to explain:
Libraries need to be rewritten to speak the new asyncio flavor of Python, and you'll need to absolutely litter your code with additional keyword noise. So much for readability counts.
A commenter on HN summed it up quite well:
I think I've made the point a few times that I don't like this style of programming at all, because the coroutine layer turns into an Inner Platform replicating all the control-flow structures the original language has, which then has to integrate with the original language which causes more than twice the complexity to emerge.
For fun, though, let's just take a peek at what adding two numbers looks like in the world of async:
import asyncio async def compute(x, y): print("Compute %s + %s ..." % (x, y)) await asyncio.sleep(1.0) return x + y async def print_sum(x, y): result = await compute(x, y) print("%s + %s = %s" % (x, y, result)) loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()
I can't wait to to start writing my very own tasks, coroutines, protocols, event loops, transports, streams, futures... Gee, do you think Django will be async any time soon?
If the signal-to-noise ratio wasn't already low enough for you, Python has decided to add type hints to the language as well. They're called hints because they are not enforced by the interpreter -- you'll need a separate tool for that -- but while you're at it you might as well just use golang (which, by the way, has a very nice language-level concurrency primitives).
Here's how you might declare a function:
def async_query(on_success: Callable[[int], None], on_error: Callable[[int, Exception], None]) -> None: # Body
That's fine and well when you're using basic types, but Python is a dynamic language...how will we handle generics, you ask? Something like this:
from typing import TypeVar, Generic, Sized, Iterable, Container, Tuple T = TypeVar('T') class LinkedList(Sized, Generic[T]): ... K = TypeVar('K') V = TypeVar('V') class MyMapping(Iterable[Tuple[K, V]], Container[Tuple[K, V]], Generic[K, V]): ...
Don't get me started on "f-strings" or the decision to break iteration for all the builtin types...
Commenting has been closed, but please feel free to contact me