Skip to content
master
Go to file
Code
This branch is 1 commit behind abrenaut:master.

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 

readme.md

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 statements

any(iterable)

data = [1, 2, 3, 4]

if any(x > 3 for x in data):
    print('Un élément est supérieur à 3')

About

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

Resources

Releases

No releases published

Packages

No packages published
You can’t perform that action at this time.