Skip to content

From Raymond Hettinger's Transforming Code into Beautiful, Idiomatic Python

Notifications You must be signed in to change notification settings

abrenaut/python-cheat-sheet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

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

range(start, stop[, step])

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

reversed(seq)

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

enumerate(iterable, start=0)

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

popitem()

d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}

while d:
    key, value = d.popitem()
    print key, '-->', value

Linking dictionaries

ChainMap

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

any(iterable) all(iterable)

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'}

About

From Raymond Hettinger's Transforming Code into Beautiful, Idiomatic Python

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published