Teeing up Python Code Golf Lee Sheng lsheng@yelp.com @bogosort - - PowerPoint PPT Presentation

teeing up python
SMART_READER_LITE
LIVE PREVIEW

Teeing up Python Code Golf Lee Sheng lsheng@yelp.com @bogosort - - PowerPoint PPT Presentation

Teeing up Python Code Golf Lee Sheng lsheng@yelp.com @bogosort Yelps Mission Connecting people with great local businesses. About Me Engineer at in London building distributed systems. Previous stints: WARNING Not actually


slide-1
SLIDE 1

Lee Sheng lsheng@yelp.com @bogosort

Teeing up Python

Code Golf

slide-2
SLIDE 2

Yelp’s Mission

Connecting people with great local businesses.

slide-3
SLIDE 3

Engineer at in London building distributed systems. Previous stints:

About Me

slide-4
SLIDE 4

WARNING

  • Not actually a golf player.
  • Monospace type ahead!
  • Not talking about traditional “code golfing”
slide-5
SLIDE 5

Code Golfing is minimizing the number of strokes in each block of code.

Code Golfing

“brevity is the soul of wit” - @WilliamShakespeare Concise code requires less cognitive load to understand the intent by avoiding implementation errata.

slide-6
SLIDE 6

It will take strokes off your code while increasing clarity.

Snake Oil1 Benefits

1: No pythons were harmed in the

making of this talk.

slide-7
SLIDE 7

+1: keywords, variable names, each

  • perator (including square brackets)

What are Strokes?

+0: whitespace, dots, commas, parentheses, quotes, colons, and closing braces/brackets Effectively counting units of information present.

slide-8
SLIDE 8

>>> import this

Why even?

The Zen of Python, by Tim Peters (abridged) Beautiful is better than ugly. Simple is better than complex. If the implementation is easy to explain, it may be a good idea.

slide-9
SLIDE 9

to_mail = "UNKNOWN" if "address" in my_contact: to_mail = my_contact["address"]

Ever written code like this?

# Try using a default

to_mail = my_contact.get("address", "UNKNOWN")

slide-10
SLIDE 10

to_maill =2 "UNKNOWN3" if4 "address5" in6 my_contact7: to_mail8 =9 my_contact10[11"address12"] # 12 strokes

Counting up the strokes

# Try using a default

to_maill =2 my_contact3.get4("address5", "UNKNOWN6") # 6 strokes

slide-11
SLIDE 11

to_mail = "UNKNOWN" if "address" in my_contact: to_mail = my_contact["address"] to_mail = my_contact.get("address", "UNKNOWN") # strokes -= 6

Visual Diff

slide-12
SLIDE 12

counts = {} if item not in counts: counts[item] = 0 counts[item] += 1

# If only there were a better way!

Initializing dict values

slide-13
SLIDE 13

counts = {} if item not in counts: counts[item] = 0 counts[item] += 1 # 18 strokes

# Why not defaultdict?

from collections import defaultdict # 4 extra strokes per file counts = defaultdict(int) counts[item] += 1 # 13 strokes (including overhead)

Initializing dict values

slide-14
SLIDE 14

infile = open('myfile.txt', 'r') for line in infile: print(line) infile.close()

# Why bother explicitly cleaning up?

Cleaning Up Resources

slide-15
SLIDE 15

infile = open('myfile.txt', 'r') for line in infile: print(line) infile.close() # 13 strokes

# Let’s do this automagically

with open('myfile.txt', 'r') as infile: for line in infile: print(line) # 12 strokes

Context Managers

slide-16
SLIDE 16

try: infile = open('myfile.txt', 'r') raise Exception() finally: infile.close() # 12 strokes

Exception Handling

# try-finally already baked in by default! with open('myfile.txt', 'r') as infile: raise Exception() # 9 strokes

slide-17
SLIDE 17

# To implement make any class into a context manager, “simply” implement # the __enter__ and __exit__ methods: class Tag(): """Poorly adds html tags""" def __init__(self, name): self.name = name def __enter__(self): print("<%s>" % self.name) def __exit__(self, *args): print("</%s>" % self.name) # Too much boilerplate, we can do better!

“Simple” implementation

slide-18
SLIDE 18

from contextlib import contextmanager @contextmanager def tag(name): """Poorly adds html tags""" print("<%s>" % name) yield # Do the actual work here print("</%s>" % name) # With enough space to spare, here’s an example: with tag("h1"): print("foo")

Let’s decorate with @contextmanager

slide-19
SLIDE 19

def cook(food): return cook( ) =>

Functions aren’t scary cook(x)

# Lambdas are just functions: lambda food:

slide-20
SLIDE 20

map([ , , ], cook) => [ , , ]

Quick Functions Primer

filter([ , , ], isVegetarian) => [ , ] reduce([ , ], eat) => Stolen from a tweet from @steveluscher

slide-21
SLIDE 21

Goofus and Gallant

slide-22
SLIDE 22

Goofus thinks iteratively, focusing on how to compute the result. Goofus has mastered looping over data to compute results.

Goofus and Gallant explore functions

Gallant thinks functionally, focusing on what the result is. Gallant has mastered composing functions to compute results.

slide-23
SLIDE 23

Goofus iterates over nums, appending doubles of values: double_nums = [] for n in nums: double_nums.append(n * 2) # 12 strokes

Goofus and Gallant explore map

Gallant uses map to compute doubles: double_nums = list(map(lambda x: x * 2, nums)) # 10 strokes

slide-24
SLIDE 24

Goofus iterates over nums, adding to the total: total = 0 for n in nums: total += n # 10 strokes

Goofus and Gallant explore reduce

Gallant uses a reducer: total = reduce(lambda x, y: x + y, nums) # 10 strokes

slide-25
SLIDE 25

Goofus iterates over nums, appending only evens:

  • nly_evens = []

for n in nums: if n % 2 == 0:

  • nly_evens.append(n)

# 16 strokes

Goofus and Gallant explore filters

Gallant filters nums for evens:

  • nly_evens = list(filter(lambda x: x % 2 == 0, nums))

# 12 strokes

slide-26
SLIDE 26
slide-27
SLIDE 27

Comprehensions are a more natural way to construct lists (and dicts). result = [] for item in things: if condition(item): result.append(transform(item)) # 14 strokes

Comprehending Comprehensions

result = [ transform(item) for item in things if condition(item) ] # 12 strokes

slide-28
SLIDE 28

result = [] for item in things: if condition(item): result.append(transform(item)) result = [ transform(item) for item in things if condition(item) ] # strokes -= 2

Comprehensions Deconstructed

slide-29
SLIDE 29

Better Mapping with Comprehensions

Gallant uses map to produce doubles: double_nums = list(map(lambda x: x * 2, nums)) # 10 strokes Billy Mays uses a comprehension: double_nums = [ x * 2 for x in nums ] # 10 strokes

slide-30
SLIDE 30

Better Filtering with Comprehensions

Gallant filters nums for evens:

  • nly_evens = list(filter(lambda x: x % 2 == 0, nums))

# 12 strokes Billy Mays uses a comprehension:

  • nly_evens = [ x for x in nums if x % 2 == 0 ]

# 14 strokes

slide-31
SLIDE 31

Better Reduces with Comprehensions

Gallant uses a reducer: total = reduce(lambda x, y: x+y, l) # 10 strokes Shamwow guy uses the sum function: total = sum(nums) # 4 strokes

slide-32
SLIDE 32

Better dicts with Comprehensions

Goofus iterates, as that’s what he knows: num_to_letters = {} for x in range(0, 26): num_to_letters[x] = chr(97 + x) # 17 strokes Billy Mays uses a comprehension: num_to_letters = {x: chr(97 + x) for x in range(0, 26)} # 14 strokes

slide-33
SLIDE 33

slides

Where can conciseness help?

screens whiteboards

slide-34
SLIDE 34

Quick Whiteboarding Tip

Instead start coding from the upper right, and you can fit 46x11 characters. If you start coding here, you’ll be awkwardly coding

  • n a 26x6 screen.
slide-35
SLIDE 35
  • Stroke reduction (making code more concise) reduces

the cognitive load to understand code.

  • Python enables doing more with less.
  • For common operations, there’s probably already a

builtin or library.

Final Takeaways

slide-36
SLIDE 36

“Je n'ai fait celle-ci plus longue que parce que je n'ai pas eu le loisir de la faire plus courte.” "I apologize for the length of this presentation, but I didn't have time to make it shorter."

  • @BlaisePascal
slide-37
SLIDE 37

www.yelp.com/careers/

We're Hiring!

slide-38
SLIDE 38

@YelpEngineering fb.com/YelpEngineers engineeringblog.yelp.com github.com/yelp

slide-39
SLIDE 39

talk.exit(“That’s all folks!”)