Hacker News new | ask | show | jobs
by peter-row 4407 days ago
Python 3 isn't really good. It's not really bad, either. There's really not that many magic bullets (other than proper functional programming, maybe, which isn't about to happen in Python).

People are leaving Python for Go because people have always left Python for fast compiled languages. Google ditched Python for C++ and Java. Java! I've seen a lot of projects get re-written in Java from Python, but no-one worried then.

Python 3 adds some cool stuff (async, in particular), and fixes some warts. It's a bit rude of them to force people to upgrade, but that will eventually pay off. It will add more things in the future.

The people who start new projects in Python 3 will have some short-term pain, as some libraries take time to port. There will be a long term benefit, though - future libraries will be better for Python 3, and they won't have to port their project.

The only controversial thing was the use of unicode. IMO, Python 3 made the right choice - you should make everything unicode where-ever it's feasible, because it's just a mess otherwise.

3 comments

"People are leaving Python for Go because people have always left Python for fast compiled languages."

I think the angst about Go comes from the fact that someone who leaves Python for Java may still come back, because you can develop far more quickly in Python than Java. But someone who leaves Python for Go probably isn't coming back... my experience is that it is slightly slower (10-20%, YMMV but we're certainly not talking integer multiples) to cut out a prototype in Go, but that said prototype runs an order of magnitude faster, can potentially be optimized without much work for another order of magnitude (though ultimately this is more a reflection of Python's slowness than Go's speed), and is then much easier to maintain and extend, even on the timescale of a week or two, to say nothing of larger time scales.

A couple of people elsewhere in the comments here assume that Python must still be much easier to develop for than Go. It really isn't anymore; it turns out the combination of garbage collection and structural-typing-esque interfaces pretty much does anything you might have ever wanted Python to do, and a great deal of the rest of the differences turn out to be far less important in practice than in theory.

I first saw the idea in Joel Spolsky's "How Microsoft Lost the API War" [1], under the heading "Automatic Transmissions Win The Day", that the primary innovation of the 1990s was simply garbage collection instead of memory management. (As he is aware the idea is older than 1990, one presumes he means that it became practical and widespread.) The languages that spread this innovation, the "scripting languages" like Perl and Python and PHP and Javascript, changed a lot of things at once, which as any good scientist knows means it was hard to tell what about those changes actually contributed to the enhanced productivity that they certainly did bring. My experience with Go certainly gives me further belief in Joel's thesis... you read a bullet point listing of the Python features and Go features and it seems obvious that Python is a wildly better language than Go, yet... I've learned after all these years and all the languages I've tried out to ask myself, if the features are so important, why don't I miss them? Because in practice, I don't. Rip closures out of Go, and I'd miss that. Rip out the goroutines and I'd miss not having something like generators or something, indeed, the language would nearly be useless to me. But I certainly don't miss metaclasses or decorators or properties. I will cop to missing pervasive protocols for the built-in types, though; I wish I could get at the [] operator for maps, or implement a truly-transparent slice object. But that's about it.

[1]: http://www.joelonsoftware.com/articles/APIWar.html

My experience was Go was about 2-3x slower (development speed) than Python for prototyping. Web programming, though, which is a particular strength of Python and a particular weakness of Go. YMMV, of course.

I actually really do miss list comprehensions and properties and pervasive protocols for built-in types and really concise keyword/literal syntax. I don't miss metaclasses, and I only vaguely miss decorators. (Go has struct annotations and compiler-available-as-a-library, which in some ways are better.) Properties in particular are really useful for the evolvability of code; otherwise you have to overdesign up front to avoid a big rewrite as you switch from straight struct fields to accessors.

I'm actually leaning towards Java 8 + Jython 2.7 as I consider what language to write my startup/personal projects in. Jython 2.7 gives me basically all the language features I actually cared about in Python, and it fixes the Unicode mess that necessitated Python 3. It has no GIL and solid multithreading support. The Java layer gives you all the speed of Go and more. And the interface between them lets you seamlessly use Java within your Jython scripts, so you can easily push code down into the Java layer without having to rewrite your whole product.

Yes, the Go web prototyping story is a bit weak. If you want to do serious development in it I think it's pretty good, because frankly for years our web frameworks in most other languages have been unbelievably contorted around not having something like goroutines in ways that we've stopped even being able to see because they're so darned pervasive, but if you just want to slam a decent site out (and there's nothing wrong with that) there's no great story there right now.
What are you considering for a prototyping datastore? Postgres? App Engine?
For prototyping I'm actually considering keeping everything in-memory, with hashtables and such (or objects that wrap them). Before you laugh, Hacker News uses this approach and it's gotten well beyond the prototyping stage.
I found to my surprise that these days Java is as productive as anything else. It's not like the early days anymore; modern Java has generics, reflection, now lambda; there would still be more finger typing than I'd ideally like, but the IDEs are more than good enough to make up for that, to the point that a nontrivial program now usually takes fewer keystrokes to write in Java than almost any other language.

Which of course reinforces your main point: nowadays the language differences just aren't that big.

I don't know why people are leaving Python for Go when they could be using PyPy or Shedskin. There are plenty of ways to be fast using Python.
Visibility probably. Every whiner blogs "I'm leaving Python for Go" but that was the first time I saw https://code.google.com/p/shedskin/ mentioned.
I think Christian derived people have a tendency to seek solutions as an externality. Rather than focus on the root cause of something and transcend it, we destroy and recreate using a different raw material. Hence leaving Python for X and not realizing that the pattern, leaving <thing> for <replacement>, will continually have one in a cycle of stagnating reconstruction.

If Python is slow, we are using it wrong.

It's easy to lose track of this in the torrent of "PyPy Benchmarks Show It Sped Up 2x!!!!" and "Latest Javascript Engine 50% Faster Than The Last One!!!! OMG!! Node!!!!", but in absolute terms, the dynamic language JITs are still quite slow. It's an urban legend that they are anywhere near compiled performance. Except LuaJIT. "Faster Python" is still slow. Unless you're in a tight loop adding numbers together, but I'm not.

Moreover, a plot of their performance over time rather strongly suggests that they've plateaued. Personally, barring any major JIT advances, I'm considering the book closed on the topic of whether JITs can take a dynamic language and make it as fast as C or C++: No.

(Recall that a Java JIT is another issue; it starts with the static typing of Java, and then JITs from there. It has a much larger array of much more powerful guarantees to work with, that the dynamic languages simply don't. Go will be running at straight-up C speeds (which it does not currently) long before Python will.)

Personally, barring any major JIT advances, I'm considering the book closed on the topic of whether JITs can take a dynamic language and make it as fast as C or C++.

It's possible to close more of the speed gap, but these JITs have to be able to:

* identify and use native primitives (avoid overflow and so forth)

* prefer stack allocation over heap (improved escape analysis)

* inline memory allocation and freeing and remove unused codepaths

* optimize across the boundary between implementation language and hosted language (which I believe requires self-hosting)

I think you're fundamentally confused about how computers work. It is always faster to do things in software than in hardware. Naive optimizations are always faster than data flow analysis from profiling. More layers of indirection make things faster, so the fastest languages put interpreters inside of their interpreters. War is peace. Slavery is freedom. The kids are getting smarter.
Why the heck are you posting stuff on this hell hole? Why am I posting stuff?
JITs are/can be faster than static code. They can make dynamic and data dependent optimizations not possible any other way.
> There's really not that many magic bullets (other than proper functional programming, maybe, which isn't about to happen in Python).

We just have to get a bit more daring; check out:

http://research.microsoft.com/pubs/211297/managedtime.pdf

Note the resemblance to Python, it's not an accident.

other than proper functional programming, maybe, which isn't about to happen in Python

If we could just have tail call optimization, I think we could make the rest work (well, maybe better lambdas, too).

> If we could just have tail call optimization, I think we could make the rest work

Manually thunk/trampoline it?

    class trampoline(object):
        def __init__(self, fn):
            self.fn = fn

        def __call__(self, *args, **kwargs):
            ret = self.fn(*args, **kwargs)
            while isinstance(ret, thunk):
                ret = ret()
            return ret


    class thunk(object):
        def __init__(self, fn, *args, **kwargs):
            self.__dict__.update(fn=fn, args=args, kwargs=kwargs)

        def __call__(self):
            if isinstance(self.fn, trampoline):
                return self.fn.fn(*self.args, **self.kwargs)
            else:
                return self.fn(*self.args, **self.kwargs)


    @trampoline
    def fact(n, accum=1):
        if n <= 1:
            return accum
        else:
            return thunk(fact, n-1, n*accum)

    print fact(1000)


    @trampoline
    def is_even(n):
        if n == 0:
            return True
        else:
            return thunk(is_odd, n - 1)


    @trampoline
    def is_odd(n):
        if n == 0:
            return False
        else:
            return thunk(is_even, n - 1)

    print is_even(1000001)


You only have to write thunk/trampoline utility once, and it is similar to how we do it in clojure.
Tail calls have already been implemented in Python using a similar technique, http://code.activestate.com/recipes/496691/