Hacker News new | past | comments | ask | show | jobs | submit login
The Zen of Python (by example) (artifex.org)
133 points by saurabh on Mar 13, 2012 | hide | past | web | favorite | 25 comments



Troll bait for Pythonistas. At least I hope that's what that is, because this needs some serious zen-ification otherwise.

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.


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.


I thought I was missing something. I sure feel better about myself now.


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.


In any case the else: is pointless.


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

  doSomething(
    identify_vertebrate(animal) if animal.is_vertebrate() \
    else identify_invertebrate(animal))

  doSomething( if( is_vertebrate(animal)
                   identify_vertebrate(animal)
                   identify_invertebrate(animal)))

  (doSomething (if (is_vertebrate animal)
                   (identify_vertebrate animal)
                   (identify_invertebrate animal)))
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.


I dunno, I like the readability of an explicit else.


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.


"The order of examples is not necessarily bad first, good second." http://news.ycombinator.com/item?id=2203101


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.

[1]: http://en.wikipedia.org/wiki/You_aint_gonna_need_it


SQLAlchemy is "complex" in both #3 and #4, so it wins in #4.


Previous discussion on HN: http://news.ycombinator.com/item?id=2203101


Hi, all. I'm Hunter Blanks (hblanks@artifex.org).

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:

1) This in now up on GitHub at: https://github.com/hblanks/talks/tree/master/2011/zen_of_pyt...

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


You can cheat in Python 2.x by giving the inner function a keyword argument that defaults to the value you want to close over:

    ...
        def count(i=i):
            ...
    ...


Won't do what's intended in this case. I tried it now just to make sure.

I'd use itertools.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.


Closures actually do work without that trick since many versions back. It's setting a nonlocal that still sucks.


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.


make_counter and make_adder are not equivalent. How can he compare the two?


    from menagerie.models import cat as cat_models
This is not recommended at all


Also, it should have read

    from menagerie.cat import models as cat_models
    from menagerie.dog import models as dog_models
    from menagerie.mouse import models as mouse_models
Because otherwise it doesn't match the code in the first example for #3, and also because if your packages are ordered that way, then why not just

    from menagerie.models import cat, dog, mouse
and forget about changing the local module name.


By whom, and why not? I like it. It's explicit. Self-documenting.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: