Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

2 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 statements

any(iterable)

data= [1, 2, 3, 4]
ifany(x>3forxindata):
print('Un élément est supérieur à 3')

About

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

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors