

The Zen of Python (by example) - saurabh
http://artifex.org/~hblanks/talks/2011/pep20_by_example.html

======
softbuilder
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.

~~~
jacobolus
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.

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

------
xerula
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.

------
ramanujan
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.

~~~
oinksoft
In any case the else: is pointless.

~~~
dustingetz
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.

------
alexatkeplar
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.

~~~
jobeirne
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>

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

------
hblanks
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...](https://github.com/hblanks/talks/tree/master/2011/zen_of_python_by_example.20110208)

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.

------
wging
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

~~~
pash
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):
                ...
        ...

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

I'd use itertools.count().

~~~
pash
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.

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

~~~
pash
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.

~~~
abecedarius
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.

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

------
tzury

        from menagerie.models import cat as cat_models
    

This is not recommended at all

~~~
iclelland
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.

