- Title: [[Raymond Hettinger - Transforming Code into Beautiful, Idiomatic Python]] - Type: #source/video - Author: [[@Raymond Hettinger]] - Reference: https://www.youtube.com/watch?v=OSGv2VnC0go - Published at: - Reviewed at: [[2021-10-06]] - Links: --- ## Basics - replace index manipulations with python's core looping idioms (when not doing numpy code) - learn advanced techniques with for-else and to arg form of iter() - aim for fast, clean idiomatic python. ### for-in Python's for loop is more like a for-each and leverages an underlying iterator construct (not index based counter). But lots of things play well with iterables (not just for-in) ```python for i in [0, 1, 2, 3, 4, 5]: print i ** 2 # in python < 2 range reifies the list, xrange is a lazy iterator # python 3 fixes that for i in range(6): print i ** 2 ``` ### loop backwards ```python for i in range(len(arr) -1, -1, -1): print arr[i] # both cleaner and faster for a in reversed(arr): print a ``` ### for with index ```python for i, a in enumerate(arr): print i, a ``` ### iterate over 2 collections `for name, color in zip(names, colors):` `izip` is similar but zips as a lazy iterator (supposedly better for L1 [[cache locality]]). Python 3's `zip` is basically `izip` so you have to force it to be iterated over or into a collection ### iterate in sorted order `for color sorted(colors):` `for color sorted(colors, reversed=True):` ```python def compare(c1, c2): ... sorted(colors, cmp=compare) # or just sort by a specific property (key) # key based sorting is faster because of fewer # custom comparator method calls sorted(colors, key=len) # python 3 no longer has custom comparator functions? ``` ### Call a function until a sentinel value ```python blocks = [] while True: block = f.read(32) if block == '': break blocks.append(block) # iter takes a second arg to denote the sentinel value bocks = [] for block in iter(partial(f.read, 32), ''): blocks.append(block) ``` ### Multiple loop exit points ```python def find(seq, target): found = False for i, value in enumerate(seq): if value == target: found = True break if not found: return -1 return i def find(seq, target): for i, value in enumerate(seq): if value == target: break else: # i.e. "no-break" # due to Donald Knuth. Calling it else made a lot of # sense when all structured programming (including for loops) # were build on conditional jumps and gotos - everyone # expected an else clause return -1 return i ``` ## Dictionary skills - Master dict fundamentals `for k in d:` iterates over keys. `for k in d.keys():` if you need to mutate the dictionary while iterating, first makes a copy `for k, v in d.items():` items returns a list of key-value tuples. `d.iteritems()` is a lazy iterator