
Essential Python Tips, Tricks, and Hacks - rockstar9
http://www.siafoo.net/article/52
======
BrandonM
The article really has something against map and filter. Specifically:

    
    
      squares_under_10 = filter(lambda x: x < 10, map(lambda x: x*x, numbers))
      
      squares_under_10 = [number*number for number in numbers if number*number < 10]
    

Those two lines are functionally different; the second one (the one the
article prefers) performs a costly multiplication twice.

I'm not saying list comprehensions are bad... I use them a ton. I just
acknowledge that there are times where map and filter make a lot of sense.
Even for trivial reasons like wrapping lines, using map allows you to break at
commas instead of embedding backslashes in your code and trying to decide how
it should all align.

If you haven't, check out the itertools standard library module. The power
held in these functional, generator-based functions is amazing. Since I
started using it, I don't know how many times I've used:

    
    
      imap(func, izip(somelist, repeat(somevalue)))
    

and other such paradigms.

Hmm... more problems with the article:

    
    
      numbers = [1,10,100,1000,10000]
      if [number for number in numbers if number < 10]:
          print 'Success'
      # Output: 'Success!'
    

This is terrible. It may look nice all on one line, but it goes through the
whole list when it doesn't need to. Better is:

    
    
      for number in numbers:
          if number < 10:
              print "Success"
              break
    

A problem with using the and ... or paradigm:

    
    
      result = test and 'Test is True' or 'Test is False'
    

The page mentions that "Test is True" must evaluate to True or else it will
always be skipped over even if test evaluates to True. It fails to mention,
however that "Test is False" must also evaluate to True. Otherwise, if test
evaluates to False, the entire expression returns False. Thus

    
    
      s = foo and "word: "+foo or ""
    

will assign False to s instead of the desired "". In this case, it is quite
clear that

    
    
      s = "word: "+foo if foo else ""
    

is preferable.

I didn't find the article all that helpful, personally. It puts forward
inefficient solutions as the recommended methods, and it doesn't offer much
that I didn't already know. The two things that I learned, which I'm not sure
that I'll ever use, are function decorators and the "string in substring"
syntax.

~~~
nostrademons
"using map allows you to break at commas instead of embedding backslashes in
your code"

I usually break list comprehensions before the 'for', and then again before
the 'if' if they still need it. No backslashes. Implicit line continuation
works as well within list comprehensions as it does in function calls.

"if [number for number in numbers if number < 10]:"

I would've used:

    
    
      if any(number < 10 for number in numbers):
    

Or alternatively

    
    
      if any(itertools.imap(lambda x: x < 10, numbers)):
    

'any' short circuits its evaluation if it finds a true item , and the
generator expression doesn't need to be evaluated fully. (I think the first is
more readable.)

I also didn't find all that much that was new and interesting in the article,
but that's because I went hunting for articles like this when I first started
getting into Python seriously, and have been programming full-time in it for 8
months now.

~~~
BrandonM
_I usually break list comprehensions before the 'for', and then again before
the 'if' if they still need it. No backslashes. Implicit line continuation
works as well within list comprehensions as it does in function calls._

Wow, I learned more from this comment than I did from the entire wiki page. I
wonder why I never realized this before... I tend to break my list
comprehensions the same way, and for some reason I thought the backslashes
were necessary.

    
    
      if any(number < 10 for number in numbers):
    

Very nice. I need to start using some of these builtins a little more. In
particular, I'm pretty sure that I've used

    
    
      for foo, index in izip(list, count())
    

before instead of simply using _enumerate_. _any_ is one that I'll definitely
have to keep in mind.

~~~
globalrev
except it is not working?

>>> numbers = [1,10,100,1000,10000,100000,1000000] >>> if any(number < 10 for
number in numbers):print number

Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> if
any(number < 10 for number in numbers):print number NameError: name 'number'
is not defined >>> if any(number < 10 for number in numbers): print number

Traceback (most recent call last): File "<pyshell#7>", line 2, in <module>
print number NameError: name 'number' is not defined >>>

~~~
jcl
It is working; the variable "number" is local to the generator expression and
can't be used in the print statement. It works if you print something else
instead (e.g. "Success!" in the article).

If you just want to print the numbers less than 10, it's easier to do:

    
    
      print [number for number in numbers if number < 10]
    

or

    
    
      print '\n'.join(str(number) for number in numbers if number < 10)
    

if you want the same formatting.

------
brianr
An improvement on the switch-using-dictionaries example: instead of doing
this...

    
    
      keycode = 2
      functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed}
      if functions.has_key(keycode):
          functions[keycode]()
      else:
          unknown_key_pressed()
    

you can save three lines by replacing the if-else block with:

    
    
      functions.get(keycode, unknown_key_pressed)()

~~~
yourmomis1337
Wow, yeah, that one is fairly obvious huh...

------
nostrademons
5.1: I prefer to use the rule "don't mutate objects" instead. I would've
defined the example as:

    
    
      def function(item, stuff=[]):
          print stuff + [item]
    

Mutating the passed-in 'stuff' creates a leaky abstraction that then has to be
documented. In rare cases this is what you want, but most of the time you're
better off creating new objects and working with them. There's less to
remember in the API, and fewer ways to screw up.

In particular, this code - which _looks_ safe - blows up horribly with the
article's function:

    
    
       MODULE_LEVEL_CONSTANT = ['foo', 'bar']
       function('baz', MODULE_LEVEL_CONSTANT)
       function('quux', MODULE_LEVEL_CONSTANT)
    

The second call will print ['foo', 'bar', 'baz', 'quux'] instead of the
expected ['foo', 'bar', 'quux'].

~~~
andreyf
Not for me...

    
    
      >>> def function(item, stuff=[]):
            print stuff + [item]
    
      >>> MODULE_LEVEL_CONSTANT = ['foo', 'bar']
      >>> function('baz', MODULE_LEVEL_CONSTANT)
      ['foo', 'bar', 'baz']
      >>> MODULE_LEVEL_CONSTANT
      ['foo', 'bar']
      >>> function('quux', MODULE_LEVEL_CONSTANT)
      ['foo', 'bar', 'quux']
    

I think you're confusing + and .append (the latter affects the object).

~~~
jaf656s
I believe he was referring to use of .append by the author. You defined it the
way he recommends, which has the desired behavior.

------
stcredzero
Less syntax in languages, please. The problem with the Perlish "There's more
than one way to do it!" is that one ends up expending extra cycles
comprehending semantic equivalence. But equivalence should be a fulcrum --
these are things leveraged to be able to reduce the effective complexity of
what we're looking at. Anything that obscures the visibility of equivalence is
bad on the balance.

~~~
earthboundkid
If your language allows both (+ 1 2) and (+ 2 1) then there's more than one
way to do it. There's always more than one way to do it. That's just the
nature of languages. Even in pure arithmetic, both 1.99999… and 2 represent
the same number. It's inescapable. The point isn't "let's only have one way to
do it." The point is to have One Obvious Way To Do It. And that's the Python
motto. What this article is doing is showing the mappings between the way you
might be used to from other languages to the preferred One Obvious Way of
Python.

Decrying syntax is nuts. Humans work better if they can pick out visually
what's going on. Even Lisp people indent their ifs and defuns. (And if Lisp
doesn't have syntax, what the hell do ' or # or (in Arc) [] do?) Now, if we
have to have at least a little syntax it's also nice if it's easy to pick out
from the visual impression of the syntax what it is that the programmer is
getting at. In my opinion, Python does a great job of this.

~~~
stcredzero
(+ 1 2) vs (+ 2 1) is a far cry from "map(lambda x: x * x, numbers)" versus
"[x * x for x in numbers]".

I call straw man. I am not asking for zero syntax, or that everything be done
in Scheme.

For one thing, I'm not a Lisper, which you think I am. For another thing, Lisp
has syntax, so you are incorrect. For yet another thing, I am decrying _Too
Much Syntax_ that encourages too many ways of doing things, not taking a
dogmatic stand for absolute syntactic minimums. True one cannot make a syntax
so pure that there is only one way of doing something. That's a straw man. But
on the other hand, one can make a syntax so complicated that it seems to
strongly encourage doing the same thing in many ways. Perl and Ruby are guilty
of this. Python not nearly as much, but still more than I would like.

Syntactic constructs are a tool. Tools have overhead. Why have redundant
syntactic tools? It's enough to get one's head around the problems to be
solved.

------
yourmomis1337
You know if you create an account on the site you can edit the article, like a
wiki.

~~~
BrandonM
That is a great idea for getting new users... make subtle mistakes in articles
and then require people to register in order to correct them.

I had actually looked for an "edit" link or a way to e-mail the original
author, but I didn't want to go through the hassle of registering before being
sure that I'd be able to fix the problems.

~~~
yourmomis1337
lol... that's true ;)

Although I'm going to try to add all the corrections you guys had when I get a
chance (probably tomorrow).

