Hacker News new | past | comments | ask | show | jobs | submit login
A Hodgepodge of Python (adku.com)
75 points by donmcc on Sept 15, 2011 | hide | past | web | favorite | 17 comments

Didn't know the timeit module. However, the presented usage to time list reversing is ill: The timing is dominated by the list creation, which should go into setup. Then I'll get:

    $ python -mtimeit -s'l=range(1000)' 'l[::-1]'      
    100000 loops, best of 3: 3.05 usec per loop
    $ python -mtimeit -s'l=range(1000)' 'l.reverse()'  
    1000000 loops, best of 3: 0.535 usec per loop
    $ python -mtimeit -s'l=range(1000)' 'a=reversed(l)' 
    1000000 loops, best of 3: 0.39 usec per loop
So, instead of reversing the list in place, creating a new, reversed list seems to be faster.

  >>> reversed([])
  <listreverseiterator object at 0xb73ff7cc>
reversed() does not create a new list, it just returns a reverse-order view of the same list. Of course, often this is exactly what is needed.

And more often than not `xrange` is what one needs, not `range`. (Not relevant with Py3k though, but everyone is still in 2.x-verse.)

    >>> xrange(13)[::-1]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: sequence index must be integer, not 'slice'

  >>> list(range(13)[::-1]) == list(xrange(12, -1, -1))

True: $ python -mtimeit -s'l=range(1000)' 'a=[i for i in reversed(l)]' 10000 loops, best of 3: 37.7 usec per loop

I always love learning little tidbits about Python like these. Thanks!

Another I found quite useful: instead of collections.defaultdict, you can use the dict's get() method to set a default value if the key doesn't exist. get() takes the key and a default value, and if the key doesn't exist it creates one with the default value provided.

  a = {}
  a['foo'] = a.get('foo',0) + 1
  # a = {'foo':1}
  a['foo'] = a.get('foo',0) + 1
  # a = {'foo':2}
It can be very useful for incrementing keys in a dict, even if they did not exist previously.

You can use the Counter class from the collections module[1] for that as well.

  >>> from collections import Counter
  >>> c = Counter()
  >>> c['foo']
  >>> c['bar'] +=1
  >>> c['bar']
[1] http://docs.python.org/dev/library/collections.html#collecti...

get() doesn't create a value in the dictionary, though; it only returns a default value. For what you describe, you would want setdefault(). It works here because you're assigning a value to a['foo'] after get()ing the default value.

I use defaultdict a lot. It's useful not only for counters, but also for sets, lists, dicts, and even other defaultdicts. It usually results in very succinct code compared to alternatives:

  occurrences = defaultdict(int)
  digraphs = defaultdict(lambda: defaultdict(int))
  prev_ch = None
  for ch in long_text:
    occurrences[ch] += 1
    if prev_ch is not None:
      digraphs[prev_ch][ch] += 1
    prev_ch = ch

  print "Occurrences:"
  for ch, count in sorted(occurrences.iteritems()):
    print "  %s: %i" % (ch, count)

  print "Digraph occurences:"
  for ch1, counts in sorted(digraphs):
    for ch2, count in sorted(counts):
      print "  %s%s: %i" % (ch1, ch2, count)
(I'm aware that digraphs could be done as a single flat dict; it's just a trivial example to show defaultdict nesting.)

I'm still on the fence as to whether this is acceptably ugly or unacceptably ugly, but it does make use of a python feature:

  >>> class PrefixStr:
  ...   def __init__(self, prefix):
  ...     self.prefix = prefix
  ...   def __getattr__(self, s):
  ...     return self.prefix+s
  >>> q = PrefixStr('xyz_')
  >>> { q.height:11, q.width:7, q.depth:3 }
  {'xyz_width': 7, 'xyz_depth': 3, 'xyz_height': 11}

The main question is whether it increases the clarity and flexibility of the code. I can't think of any contexts where it would, but I wouldn't rule it out.

The more I use Python the more I like it. It allows a lot of the early refactorings needed for a proof of concept prototype. Playing with it feels like playing with a well lubrificated rubix cube, it rotates well in all directions, allowing quick changes in the code's structure. I can't tell exactly why but I think the use of "self" in object methods, while a bit weird in the begining, is one of the best part. Eg morphing a helper function into a method is just a matter of one search and replace.

This is the stuff that brings me back to hacker news everyday. So helpful.

izip is the super useful generator counterpart of zip

Also, 2.7 introduced a couple of cool things in collections: OrderedDict and Counter

> In my mind, dynamic typing = bugs, interpreted language = slow. Ahhhhh!

He explained why he changed his mind on performance, but not whether or not "dynamic typing = bugs". This would've been interesting to read about from a recent convert.

Thanks for posting these tips!

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