Indeed. Half of it seems to be bashing popular libraries the author doesn’t like, and the other half is a mixture of extremely contrived examples, and examples that miss the point of whatever particular part of the Zen they’re supposed to illustrate.
Even as a fledgling Python coder, I found this distinctly unenlightening. But, as a biologist, the part that really bugged me were the functions that regard yeast as invertebrate animals.
The "identify this animal" example has a serious bug. This:
def identify(animal):
if animal.is_vertebrate():
return identify_vertebrate()
else:
return identify_invertebrate()
should be
def identify(animal):
if animal.is_vertebrate():
return identify_vertebrate(animal)
else:
return identify_invertebrate(animal)
Arguably this mix of OO and procedural code is bad as well. But less arguably, the initial example (with a simple identify function) was far better than the rewrite.
its subjective, arguably a style using ternary operator is better because it is composable - it evaluates as an expression, as opposed to a series of statements. it's something the lisp and functional programming people like - no implied dependency on order in which statements execute.
i dunno if this is valid python but it demonstrates the idea
this is part of "referential transparency", and once you get past "OMG parens" you realize its a Good Thing. parens are caused by a style preferring composition of expressions over executing statements, not lisp.
In #3, SQLAlchemy loses because it's "complex" while JSON is "simple". In #4, SQLAlchemy loses because this time it's "complicated", whereas inlining SQL create table statements is merely "complex".
Conclusion: for Hunter Blanks, SQLAlchemy can never win.
IMO, #3 was more about YAGNI[1], i.e. you probably won't need the speed, integrity constraints, relations, etc. delivered by SQL, so keep your data model flexible and lightweight until a shortage of those things is actually getting in your way.
I wouldn't be surprised if many people criticizing #3 had no practical experience with NoSQL and the benefits brought by its lack of baggage.
Folks seem to have found a lot of implicit meaning and bugs in what was, at its grandest, a 5 minute talk at RedSnake Philly 2011. I'll just make two comments:
so if you feel strongly about making this better, you should send me a pull request.
2) People seem to have decided that the top (or the bottom?) of every example is the more pythonic one. I suppose I should have kept a consistent order, but, as I noted in the initial HN thread, that just wasn't necessary since this was delivered in the context of a talk.
This doesn't work. I think it's an attempt at a closure, which you can do in the latest Python 3.x by `nonlocal i` as the first statement in the function definition...
def make_counter():
i = 0
def count():
""" Increments a count and returns it. """
i += 1
return i
return count
Yes, I just meant to point out that there is a way to do closures in Python 2.x. Looks like here the author wants a function that saves its internal state, i.e., a generator.
Well, they sort of work. Closures don't work properly [0] in some lexical contexts without using the default-argument trick because of the way function definition works in Python:
fs = []
for i in range(5):
def f():
print(i)
fs.append(f)
gs = []
for i in range(5):
def g(i=i):
print(i)
gs.append(g)
The functions in `fs` all print 4 because they all refer to the final object assigned to `i` in the `for` loop. The functions in `gs`, on the other hand, print 0 through 4, as they "should" because the `i` in `g` is assigned to the object currently referenced by the `for` loop's `i` each time the `def` statement is evaluated. This distinction in behavior is obscured in the classic function-inside-a-function example of closures because you pass the object to be closed over to the outer function each time you want to create a new closure. So the result is analogous to using the default-argument trick in the example above.
Anyway, that's all I meant to point out. I misinterpreted your reference to `nonlocal`, which I'm not very familiar with as I don't really use Python 3 much yet. So sorry for my nonsensical reply.
[0] By "properly" I mean as closures work in pure functional languages, where they originated. I realize a programmer who understands Python's data model shouldn't generally expect them to work that way in Python.
Oh, yes, good point, though I wouldn't call it a problem with how closures work -- Lisp has the same issue, for example, if you did a (dolist) or (loop) just like your Python code's 'for' loop. In both cases we have a language not originally designed for a style using frequent closures and running into some gotchas thereby.
My answers above were really short because I was on a tablet. May have come across as curt.
To summarize, while switching religions: "The Tao that can be expressed is not the eternal Tao".
But if you're going to try to express the Zen of Python, you can do a hell of a lot better than this.