10 tips for writing pythonic code by michael kennedy
TRANSCRIPT
Topics1. Dictionaries for performance2. __slots__3. Merge dicts4. yield and generator methods5. lambda expressions6. adding iteration to custom types7. comprehensions and expressions8. json back and forth (files and web services)9. slicing (collections and databases)10. yield + recursion
1. Dictionary for performance
data_list = [ ... ] # len = 500,000 itemsinteresting_points = [ ... ] # len = 100 itemsfor i in interesting_ids:
pt = find_point_by_id_in_list(data_list, i)interesting_points.append(pt)
data_lookup = dict( ... ) # len = 500,000 itemsinteresting_points = [ ... ] # len = 100 itemsfor i in interesting_ids:
pt = data_lookup[i]interesting_points.append(pt)
What is the relative performance of these two algorithms?
2. Memory efficiency with slots
Saving 9 GB of RAM with Python’s __slots__http://tech.oyster.com/save-ram-with-python-slots/
2. Memory efficiency with slots
class ImmutableThing:__slots__ = ['a', 'b', 'c']
def __init__(self, a, b, c):self.a = aself.b = bself.c = c
Defining __slots__ restricts values allowed in type but removes per instance dictionary backing story.
Special cases aren't special enough to break the rules.Although practicality beats purity.
3. Merging dictionaries
route = {'id': 271, 'title': 'Fast apps'}query = {'id': 1, 'render_fast': True}post = {'email': '[email protected]', 'name': 'Jeff'}
# Non-pythonic procedural waym1 = {}for k in query:
m1[k] = query[k]for k in post:
m1[k] = post[k]for k in route:
m1[k] = route[k]
3. Merging dictionaries
route = {'id': 271, 'title': 'Fast apps'}query = {'id': 1, 'render_fast': True}post = {'email': '[email protected]', 'name': 'Jeff'}
m1 = {**query, **post, **route}
Py3
4. Generators with yield
def classic_fibonacci(limit):nums = []current, nxt = 0, 1
while current < limit:current, nxt = nxt, nxt + currentnums.append(current)
return nums
4. Generators with yield
def fibonacci_generator():current, nxt = 0, 1while True:
current, nxt = nxt, nxt + currentyield current
5. Passing function expressions
def find_special_numbers(special_selector, limit=10):found = []n = 0while len(found) < limit:
if special_selector(n):found.append(n)
n += 1return found
find_special_numbers(lambda x: x % 6 == 0)
6. Adding custom iterationclass ShoppingCart:
def __init__(self):self.items = []
def add_item(self, it):self.items.append(it)
def __iter__(self):for it in sorted(self.items, key=lambda i: i.price):
yield it
cart = ShoppingCart()cart.add_item(CartItem("guitar", 799))cart.add_item(CartItem("cd", 19))cart.add_item(CartItem("iPhone", 699))
for item in cart:print('{} for ${}'.format(item.name, item.price))
7. Comprehensions everywhere
# direct loop stylehigh_measurements = []for m in measurements:
if m.value >= 70:high_measurements.append(m.value)
7. Comprehensions everywhere
# generator stylehigh_measurements = (
m.valuefor m in measurementsif m.value >= 70
)
8. JSON to dictionaries and back
movie_json = """{
"Title":"Johnny 5","Year":"2001","Runtime":"119 min","Country":"USA"
}"""
movie_data = json.loads(movie_json)# movie_data is a dict
movie_json2 = json.dumps(movie_data)# movie_json2 is a str
9. Slicingnums = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 ]
num_2_to_7 = nums[2:8]# num_2_to_7 = [2, 3, 5, 8, 13, 21]
num_last_3 = nums[-3:]# num_last_3 = [89, 144, 233]
10. yield fromdef get_files(folder):
for item in os.listdir(folder):full_item = os.path.join(folder, item)
if os.path.isfile(full_item):yield full_item
elif os.path.isdir(full_item):yield from get_files(full_item)
Py3