GitHub - abrenaut/python-cheat-sheet: From Raymond Hettinger's Transforming Code into Beautiful, Idiomatic Python
The Python Way
From Raymond Hettinger's Transforming Code into Beautiful, Idiomatic Python
Loops
Looping over a range of numbers
Whenever you're manipulating indices directly, you're probably doing it wrong
for i in [0, 1, 2, 3, 4, 5]: print i**2 # the python way # range() takes a small amount of memory because it calculates individual items as needed for i in range(6): print i**2
Looping over a collection
colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)): print colors[i] # the python way for color in colors: print color
Looping backwards
colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)-1, -1, -1): print colors[i] # the python way for color in reversed(colors): print color
Looping over a collection and indices
colors = ['red', 'green', 'blue', 'yellow'] for i in range(len(colors)): print i, '-->', colors[i] # the python way for i, color in enumerate(colors): print i, '-->', color
Looping over two collections
zip(*iterables): Makes an iterator that aggregates elements from each of the iterables.
names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] n = min(len(names), len(colors)) for i in range(n): print names[i], '-->', colors[i] # the python way for name, color in zip(name, colors): print name, '-->', color
Nested loops
itertools.product(*iterables, repeat=1): Cartesian product of input iterables.
names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] for name in names: for color in colors: print(name, color) # the python way from itertools import product products = product(names, colors) for name, color in products: print(name, color)
Looping in sorted order
sorted(iterable[, key][, reverse])
colors = ['red', 'green', 'blue', 'yellow'] for color in sorted(colors): print color # reverse order for color in sorted(colors, reverse=True): print color # custom order for color in sorted(colors, key=len): print color
Call a function until a sentinel value
As soon as you've made something iterable, it works with all of the Python toolkit
iter(object[, sentinel]) functools.partial(func, *args, **keywords)
# sentinel value the traditional way blocks = [] while True: block = f.read(32) if block = '': break blocks.append(block) # the second argument of the iter function is a sentinel value # in order to make it work, the first function has to be a function with no arguments, hence the partial blocks = [] for block in iter(partial(f.read, 32), ''): blocks.append(block)
Distinguishing multiple exit points in loops
The for loop else should have been called nobreak
def find(seq, target): found = False for i, value in enumerate(seq): if value == tgt: found = True break if not found: return -1 return i def find(seq, target): for i, value in enumerate(seq): if value == tgt: break else: return -1 return i
Dictionaries
Looping over dictionary keys
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'} for k in d: print k for k in d.keys(): if k.startswith('r'): del d[k]
Looping over a dictionary keys and values
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'} for k in d: print k, '-->', d[k] # the python way for k, v in d.items(): print k, '-->', v
Construct a dictionary from pairs
names = ['raymond', 'rachel', 'matthew'] colors = ['red', 'green', 'blue', 'yellow'] d = dict(zip(names, colors))
Counting with dictionaries
class collections.defaultdict([default_factory[, ...]])
colors = ['red', 'green', 'red', 'blue', 'green', 'red'] d = {} for color in colors: if color not in d: d[color] = 0 d[color] += 1 d = {} for color in colors: d[color] = d.get(color, 0) + 1 # the python way d = defaultdict(int) for color in colors: d[color] += 1
Grouping with dictionaries
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie'] d = {} for name in names: key = len(name) if key not in d: d[key] = [] d[key].append(name) d = {} for name in names: key = len(name) d.setdefault(key, []).append(name) # the python way d = defaultdict(list) for name in names: key = len(name) d[key].append(name)
Remove and return a (key, value) pair from a dictionary
popitem() is atomic so it can be used bewteen threads
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'} while d: key, value = d.popitem() print key, '-->', value
Linking dictionaries
defaults = {'color': 'red', 'parser': 'guest'} parser = argparse.ArgumentParser() parser.add_argument('-u', '--user') parser.add_argument('-c', '--color') namespace = parser.parse_args([]) command_line_args = {k: v for k, v in vars(namespace).items() if v} d = default.copy() d.update(os.environ) d.update(command_line_args) # the python way d = ChainMap(command_line_args, os.environ, defaults)
Clarity
Keyword arguments
twitter_search('@obama', False, 20, True) # the python way twitter_search('@obama', retweets=False, numtweets=20, popular=True)
Named tuples
doctest.testmod() # output: (0, 4) TestResults = namedtuple('TestResults', ['failed', 'attempted']) doctest.testmod() # output: TestResults(failed=0, attempted=4)
Unpacking sequences
p = 'Raymond', 'Hettinger', 0x30, 'python@example.com' fname = p[0] lname = p[1] age = p[2] email = p[3] # the python way fname, lname, age, email = p
Updating multiple state variables
def fibonacci(n): x = 0 y = 1 for i in range(n): print x t = y y = x + y x = t # the python way def fibonnaci(n): x, y = 0, 1 for i in range(n): print x x, y = y, x + y
Efficiency
Concatening strings
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie'] s = names[0] for name in name[1:]: s += ', ' + name print s # the python way ', '.join(names)
Updating sequences
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie'] # whenever you use this you should be using a deque instead del names[0] names.pop(0) names.insert(0, 'mark') names = deque(['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie']) del names[0] names.popleft() names.appendleft('mark')
Decorators and Context Managers
@functools.lru_cache(maxsize=128, typed=False)
def web_lookup(url, saved={}): if url in saved: return saved[url] page = urllib.urlopen(url).read() saved[url] = page return page # the python way @lru_cache def web_lookup(url): return urllib.urlopen(url).read()
Factor-out temporary contexts
Anytime your setup and teardown logic get repeated in your code you want a context manager to improve it
old_context = getcontext().copy() getcontext().prec = 50 print Decimal(355) / Decimal(113) setcontext(old_context) # the python way with localcontext(Context(prec=50)): print Decimal(355) / Decimal(113) try: os.remove('somefile.tmp') except OSError: pass # the python way @contextlib.contextmanager def ignored(*exceptions): try: yield except exceptions: pass with ignored(OSError): os.remove('somefile.tmp')
Concise Expressive One-Liners
One logical line of code equals one sentence in English
Built-ins
Replace multiple OR / AND statements
data = [1, 2, 3, 4] if any(x > 3 for x in data): print('An element is bigger than 3') if all(x > 0 for x in data): print('All elements are bigger than 0')
Asterisks in Python
From Trey Hunner's Asterisks in Python: what they are and how to use them
Python’s * and ** operators aren’t just syntactic sugar. Some of the things they allow you to do could be achieved through other means, but the alternatives to * and ** tend to be more cumbersome and more resource intensive.
Asterisks for unpacking into function call
fruits = ['lemon', 'pear', 'watermelon', 'tomato'] print(*fruits) # output: lemon pear watermelon tomato
# The ** operator does something similar, but with keyword arguments date_info = {'year': "2020", 'month': "01", 'day': "01"} filename = "{year}-{month}-{day}.txt".format(**date_info)
Asterisks for packing arguments given to function
from random import randint def roll(*dice): return sum(randint(1, die) for die in dice) roll(6, 6) # output: 9
# we can use ** when defining a function to capture any keyword arguments given to the function into a dictionary def tag(tag_name, **attributes): attribute_list = [ f'{name}="{value}"' for name, value in attributes.items() ] return f"<{tag_name} {' '.join(attribute_list)}>" tag('a', href="http://treyhunner.com") # output: '<a href="http://treyhunner.com">'
Asterisks in tuple unpacking
numbers = [1, 2, 3, 4, 5, 6] first, *rest = numbers print(rest) # output: [2, 3, 4, 5, 6]
Asterisks in list literals
# use * to dump an iterable into a new list fruits = ['lemon', 'pear', 'watermelon', 'tomato'] uppercase_fruits = (f.upper() for f in fruits) [*fruits, *uppercase_fruits] # output: ['lemon', 'pear', 'watermelon', 'tomato', 'LEMON', 'PEAR', 'WATERMELON', 'TOMATO']
# use * to dump a dictionary into a new dictionary date_info = {'year': "2020", 'month': "01", 'day': "01"} track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'} {**date_info, **track_info} # output: {'year': '2020', 'month': '01', 'day': '01', 'artist': 'Beethoven', 'title': 'Symphony No 5'} # merge dictionaries while overriding particular values event_info = {'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'} new_info = {**event_info, 'day': "14"} new_info # output: {'year': '2020', 'month': '01', 'day': '14', 'group': 'Python Meetup'}