From Raymond Hettinger's Transforming Code into Beautiful, Idiomatic Python
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
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print colors[i]
# the python way
for color in colors:
print color
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
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
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
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)
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
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)
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
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]
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
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
d = dict(zip(names, colors))
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
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)
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
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)
twitter_search('@obama', False, 20, True)
# the python way
twitter_search('@obama', retweets=False, numtweets=20, popular=True)
doctest.testmod()
# output: (0, 4)
TestResults = namedtuple('TestResults', ['failed', 'attempted'])
doctest.testmod()
# output: TestResults(failed=0, attempted=4)
p = 'Raymond', 'Hettinger', 0x30, '[email protected]'
fname = p[0]
lname = p[1]
age = p[2]
email = p[3]
# the python way
fname, lname, age, email = p
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
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)
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')
@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()
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')
One logical line of code equals one sentence in English
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')
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.
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)
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">'
numbers = [1, 2, 3, 4, 5, 6]
first, *rest = numbers
print(rest)
# output: [2, 3, 4, 5, 6]
# 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'}