Skip to content

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])

foriin [0, 1, 2, 3, 4, 5]:
printi**2# the python way# range() takes a small amount of memory because it calculates individual items as neededforiinrange(6):
printi**2

Looping over a collection

colors= ['red', 'green', 'blue', 'yellow']
foriinrange(len(colors)):
printcolors[i]
# the python wayforcolorincolors:
printcolor

Looping backwards

reversed(seq)

colors= ['red', 'green', 'blue', 'yellow']
foriinrange(len(colors)-1, -1, -1):
printcolors[i]
# the python wayforcolorinreversed(colors):
printcolor

Looping over a collection and indices

enumerate(iterable, start=0)

colors= ['red', 'green', 'blue', 'yellow']
foriinrange(len(colors)):
printi, '-->', colors[i]
# the python wayfori, colorinenumerate(colors):
printi, '-->', 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))
foriinrange(n):
printnames[i], '-->', colors[i]
# the python wayforname, colorinzip(name, colors):
printname, '-->', color

Nested loops

itertools.product(*iterables, repeat=1): Cartesian product of input iterables.

names= ['raymond', 'rachel', 'matthew']
colors= ['red', 'green', 'blue', 'yellow']
fornameinnames:
forcolorincolors:
print(name, color)
# the python wayfromitertoolsimportproductproducts=product(names, colors)
forname, colorinproducts:
print(name, color)

Looping in sorted order

sorted(iterable[, key][, reverse])

colors= ['red', 'green', 'blue', 'yellow']
forcolorinsorted(colors):
printcolor# reverse orderforcolorinsorted(colors, reverse=True):
printcolor# custom orderforcolorinsorted(colors, key=len):
printcolor

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 wayblocks= []
whileTrue:
block=f.read(32)
ifblock='':
breakblocks.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 partialblocks= []
forblockiniter(partial(f.read, 32), ''):
blocks.append(block)

Distinguishing multiple exit points in loops

The for loop else should have been called nobreak

deffind(seq, target):
found=Falsefori, valueinenumerate(seq):
ifvalue==tgt:
found=Truebreakifnotfound:
return-1returnideffind(seq, target):
fori, valueinenumerate(seq):
ifvalue==tgt:
breakelse:
return-1returni

Dictionaries

Looping over dictionary keys

d= {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
forkind:
printkforkind.keys():
ifk.startswith('r'):
deld[k]

Looping over a dictionary keys and values

d= {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
forkind:
printk, '-->', d[k]
# the python wayfork, vind.items():
printk, '-->', 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= {}
forcolorincolors:
ifcolornotind:
d[color] =0d[color] +=1d= {}
forcolorincolors:
d[color] =d.get(color, 0) +1# the python wayd=defaultdict(int)
forcolorincolors:
d[color] +=1

Grouping with dictionaries

names= ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie']
d= {}
fornameinnames:
key=len(name)
ifkeynotind:
d[key] = []
d[key].append(name)
d= {}
fornameinnames:
key=len(name)
d.setdefault(key, []).append(name)
# the python wayd=defaultdict(list)
fornameinnames:
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'}
whiled:
key, value=d.popitem()
printkey, '-->', 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: vfork, vinvars(namespace).items() ifv}
d=default.copy()
d.update(os.environ)
d.update(command_line_args)
# the python wayd=ChainMap(command_line_args, os.environ, defaults)

Clarity

Keyword arguments

twitter_search('@obama', False, 20, True)
# the python waytwitter_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 wayfname, lname, age, email=p

Updating multiple state variables

deffibonacci(n):
x=0y=1foriinrange(n):
printxt=yy=x+yx=t# the python waydeffibonnaci(n):
x, y=0, 1foriinrange(n):
printxx, y=y, x+y

Efficiency

Concatening strings

names= ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie']
s=names[0]
fornameinname[1:]:
s+=', '+nameprints# 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 insteaddelnames[0]
names.pop(0)
names.insert(0, 'mark')
names=deque(['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie'])
delnames[0]
names.popleft()
names.appendleft('mark')

Decorators and Context Managers

@functools.lru_cache(maxsize=128, typed=False)

defweb_lookup(url, saved={}):
ifurlinsaved:
returnsaved[url]
page=urllib.urlopen(url).read()
saved[url] =pagereturnpage# the python way@lru_cachedefweb_lookup(url):
returnurllib.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=50printDecimal(355) /Decimal(113)
setcontext(old_context)
# the python waywithlocalcontext(Context(prec=50)):
printDecimal(355) /Decimal(113)
try:
os.remove('somefile.tmp')
exceptOSError:
pass# the python way@contextlib.contextmanagerdefignored(*exceptions):
try:
yieldexceptexceptions:
passwithignored(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]
ifany(x>3forxindata):
print('An element is bigger than 3')
ifall(x>0forxindata):
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 argumentsdate_info= {'year': "2020", 'month': "01", 'day': "01"}
filename="{year}-{month}-{day}.txt".format(**date_info)

Asterisks for packing arguments given to function

fromrandomimportrandintdefroll(*dice):
returnsum(randint(1, die) fordieindice)
roll(6, 6)
# output: 9
# we can use ** when defining a function to capture any keyword arguments given to the function into a dictionarydeftag(tag_name, **attributes):
attribute_list= [
f'{name}="{value}"'forname, valueinattributes.items()
]
returnf"<{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=numbersprint(rest)
# output: [2, 3, 4, 5, 6]

Asterisks in list literals

# use * to dump an iterable into a new listfruits= ['lemon', 'pear', 'watermelon', 'tomato']
uppercase_fruits= (f.upper() forfinfruits)
[*fruits, *uppercase_fruits]
# output: ['lemon', 'pear', 'watermelon', 'tomato', 'LEMON', 'PEAR', 'WATERMELON', 'TOMATO']
# use * to dump a dictionary into a new dictionarydate_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 valuesevent_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

39 stars

Watchers

3 watching

Forks

Releases

Packages

Contributors