Skip to content

Instantly share code, notes, and snippets.

@grafuls
Last active November 9, 2022 15:04
Show Gist options
  • Save grafuls/b8468cc374a258f83572 to your computer and use it in GitHub Desktop.
Save grafuls/b8468cc374a258f83572 to your computer and use it in GitHub Desktop.
Python Best practices
# substracted from https://www.youtube.com/watch?v=OSGv2VnC0go among others
# Raymond Hettinger
# Learn to take better advantage of Python's best features and improve existing code through a series of code transformations,
# "When you see this, do that instead."
from itertools import izip
from functools import partial
from functools import wraps
from collections import defaultdict
from collections import deque
from decimal import localcontext
import random
#Python 3.3
#from collections import ChainMap
import argparse
"""looping over a collection"""
names = ['raymond', 'rachel', 'mathew']
colors = ['red', 'green', 'blue', 'yellow']
#NO
#for i in range(len(colors)):
#print colors[i]
for color in colors:
print color
"""looping backwards"""
#NO
#for i in range(len(colors)-1, -1, -1):
#print colors[i]
for color in reversed(colors):
print color
"""looping over collection and indicies"""
#NO
#for i in range(len(colors)):
#print i, '-->', colors[i]
for i, color in enumerate(colors):
print i, '-->', colors[i]
"""looping over 2 collections"""
#NO
#n = min(len(names), len(colors))
#for i in range(n):
#print names[i], '-->', colors[i]
#NO
#for name, color in zip(names, colors):
#print name, '-->', color
for name, color in izip(names, colors):
print name, '-->', color
"""looping in sorted order"""
for color in sorted(colors):
print color
for color in sorted(colors, reverse=True):
print color
"""custom sort order"""
#NO
#def compare_length(c1,c2):
#if len(c1) < len(c2): return -1
#if len(c1) > len(c2): return 1
#return 0
#print sorted(colors, cmp=compare_length)
print sorted(colors, key=len)
"""call a function until a sentinel value"""
blocks = []
#NO
#while True:
#block = f.read(32)
#if block == '':
#break
#blocks.append(block)
#YES
#for block in iter(partial(f.read, 32), ''):
#blocks.append(block)
"""distinguishing multiple exit points in loops"""
#NO
#def find(seq, target):
#found = False
#for i, value in enumerate(seq):
#if value == target:
#found = True
#break
#if not found:
#return -1
#return i
def find(seq, target):
for i, value in enumerate(seq):
if value == target:
break
else:
return -1
return i
"""looping over dictionary keys"""
#if you mutate a dictionary while you are iterating through it you are living in a state of sin
d = {'mathew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print k
for k in d.keys():
if k.startswith('r'):
del d[k]
d = {k : d[k] for k in d if not k.startswith('r')}
"""looping over a dictionary keys and values"""
#for k in d:
#print k, '-->', d[k]
#this will create a big list
#for k, v in d.items():
#print k, '-->', v
for k, v in d.iteritems():
print k, '-->', v
"""construct a dictionary from pairs"""
d = dict(izip(names, colors))
#{'mathew': 'blue', 'rachel': 'green', 'raymond': 'red'}
"""counting with dictionaries"""
#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
#TODO: distinctions between regular dictionary and default dict
#TODO: factory functions
d = defaultdict(int)
for color in colors:
d[color] += 1
"""grouping with dicts"""
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'chaarlie']
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)
d = defaultdict(list)
for name in names:
key = len(name)
d[key].append(name)
"""is dict popitem() atomic?"""
d = {'mathew': 'blue', 'rachel': 'green', 'raymond': 'red'}
while d:
key, value = d.popitem()
print key, '-->', value
"""linking dictionaries"""
defaults = {'color': 'red', 'user': '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}
#NO
#if you want your code to be fast don't copy so much
#d = defaults.copy()
#d.update(os.environ)
#d.update(command_line_args)
#Python 3.3
#YES!!!
print 'use chainmap!!!'
#d = ChainMap(command_line_args, os.environ, defaults)
"""improving clarity"""
"""clarity funtion calls with keyword arguments"""
#twitter_search('@obama', False, 20, True)
#YES!!!
print 'use function calls with keyword arguments'
#twitter_search('@obama', retweets=False, numtweets=20, popular=True)
"""clarify multiple return valures with named tuples"""
#
#doctest.testmod()
#(0, 4)
#named tuples are a sub class of tuple
#YES!!!
print 'use named tuples!'
#doctest.testmod()
#TestResults(failed=0, attempted=4)
#how?
#TestResults = namedtuple('TestRestls', ['failed', 'attempted'])
"""unpacking sequences"""
p = 'Raymond', 'Hettinger', 0x30, '[email protected]'
#fname = p[0]
#lname = p[1]
#age = p[2]
#email = p[3]
fname, lname, age, email = p
"""updating multiple state variables"""
#NO
#def fibonacci(n):
#x = 0
#y = 1
#for i in range(n):
#print x
#t = y
#y = x + y
#x = t
#q: always xrange?
#a: http://stackoverflow.com/questions/135041/should-you-always-favor-xrange-over-range
def fibonacci(n):
x, y = 0, 1
for i in range(n):
print x
x, y = y, x + y
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
"""concatenating strings"""
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'chaarlie']
#NO
#s = names[0]
#for name in names[1:]:
#s += ', ' + name
#print s
print ', '.join(names)
"""updating sequences"""
#del names[0]
#names.pop(0)
#names.insert(0, 'mark')
names = deque(['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'chaarlie'])
del names[0]
names.popleft()
names.appendleft('mark')
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
"""decorators and context managers"""
"""using decorators to factor-out administrative logic"""
#def web_lookup(url, saved={}):
#if url in saved:
#return saved[url]
#page = urllib.urlopen(url).read()
#saved[url] = page
#return page
def cache(func):
saved = {}
@wraps(func)
def newfunc(*args):
if args in saved:
return newfunc(*args)
result = func(*args)
saved[args] = result
return result
return newfunc
@cache
def web_lookup(url):
return urllib.urlopen(url).read()
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
"""factor-out temporary contexts"""
#old_context = getcontext().copy()
#getcontext().prec = 50
#print Decimal(355) / Decimal(113)
#setcontext(old_context)
#context manager
print 'use context manager!!!'
#with localcontext(Context(prec=50)):
#print Decimal(355) / Decimal(113)
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
"""how to open and close files"""
#f = open('data.txt')
#try:
#data = f.read()
#finally:
#f.close()
with open('data.txt') as f:
data = f.read()
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
"""how to use locks"""
# Make a lock
# TODO: threading.lock() ????
lock = threading.Lock()
#Old way to use a lock
#lock.acquire()
#try:
#print 'Critical section 1'
#print 'Critical section 2'
#finally:
#lock.release()
#new awesome way to use it
with lock:
print 'Critical section 1'
print 'Critical section 2'
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#NO
#try:
#os.remove('somefile.txt')
#except OSError:
#pass
with ignored(OSError):
os.remove('somefile.txt')
#add this to your utils directory
@contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#with open('help.txt', 'w') as f:
#oldstdout = sys.stdout
#sys.stdout = f
#try:
#help(pow)
#finally:
#sys.stdout = oldstdout
#TODO: stdout?
with open('help.txt', 'w') as f:
with redirect_stdout(f):
help(pow)
@contextmanager
def redirect_stdout(fileobj):
oldstdout = sys.stdout
sys.stdout = fileobj
try:
yield fileobj
finally:
sys.stdout = oldstdout
"""list comprehensions and generator expressions"""
#result = []
#for i in range(10):
#s = i ** 2
#result.append(s)
#print sum(result)
#single unit of thought
#print sum([i ** 2 for i in xrange(10)])
#better way
print sum(i ** 2 for i in xrange(10))
#TODO: metaclasess?
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#string concatanation
foo = 'foo'
bar = 'bar'
foobar = '%s%s' % (foo, bar) # It is OK
foobar = '{0}{1}'.format(foo, bar) # It is better
foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # It is best
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#Select a random item from a list/tuple/data stucture in Python
items = ['here', 'are', 'some', 'strings', 'of', 'which', 'we', 'will', 'select', 'one']
#NO
#rand_item = items[random.randrange(len(items))]
#NEITHER
#rand_items = [items[random.randrange(len(items))] for item in range(4)]
#YES
rand_item = random.choice(items)
#Why not?
rand_items = random.sample(items, n)
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#NO
#if 'xyz' in self.attr:
#self.attr.remove('xyz')
#YES
try:
self.attr.remove('xyz')
except ValueError:
pass
#Second way is a bit more pythonic as it follows the "It's easier to ask for forgiveness than for permission" principle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment