Just like any other form of art, Programming can always be improved. In daily grind to deliver working software we might forget details and `JUST DO IT`. In our quest to improve our python coding we've come across couple of good Python videos that will help you take you code to next level.
Following is summary of two talks Designing Poetic APIs by Erik Rose and Transforming Code into Beautiful, Idiomatic Python by Raymond Hettinger. On twitter you can find them on @erikrose and @raymondh
Designing Poetic APIs by Erik Rose
Eric Rose’s Github has more fascinating stuff. His talk is a good investment of 37 minutes of your time. Following is summary we’ve compiled from the video:
Don’t Be An Architecture Astronaut
- Best libraries are extracted and not invented
Consistency
- User’s spend 90% of time calling other people’s APIs
- Sticking to commonly used conventions can help
- E.g. Macintosh Human Interface Design is a good example of how consistency in terms of Keyboards and Menus were extracted
- API Design is similar to UI Design and principles from UI Design can be applied to API Design
get(key, default) is better than fetch(default, key)
because it sticks with current conventions of Python
- Layout all options and select the best one
- If you’re going to be wiered, be self consistent
- Red flags for Consistency: - Frequent references to your own documentation - Feel syntactically correct {Make sure novely pays off than shows off}
Brevity
- Try to make common things shorts
- Red flags for Brevity: - Copy and Pasting same piece of code frequently - Typing something irrelevant - Long arg list, suggest lack of sane defaults
Composability
- Being able to reuse code and use in different setting
- Aka Flexibility, Loose Coupling
- Red flags for Composability - Classes with lot of state - Deep inheritance heirarchy - Violations of Law of Dementer - Mocking in test {too many dependencies as means of Mocking is not a good idea} - Options {a lot of them}
Plain Data
- Avoid using complex/custom data structures. Use as many as standards data structures
- Try to use as many build-in data structures
- Red flags for Plain Data - If users take your data and convert it to “something”, output should be that “something” - Instantiating objects to pass to another one - Rewriteing language-provided things
Groviness
- Allow users to spend more time in groviness
- Avoid nonsense representations
- Fail shallowly
- Resource acquisition in initialization - {Required explicitness, error if no required argument are provided}
- Compelling Examples
- Red falgs for Groviness - Representable nonsense - Invariants that aren’t - Lack of clear starting point - Long, complicated documentation
Safety
- Walls are there for preveting hurting yourself and others
- Red flags for Safety - Docs that say “remember to”… or “make sure you…” - Surprisingly people will blame themselves {Which points to bad design}
Transforming Code into Beautiful, Idiomatic Python by Raymond Hettinger
RDH Talks is interesting list. His talk is another interesting one. Here is the summary
When you see this, do that instead
- Replace traditional index manipulation with Python’s core looping idioms
- Learn advanced techniques with for-else clause and two arguments of iter()
- Improve your craftsmanship and aim for clean, fast, idiomatic Python code
-
Looping over a range of numbers
for i in [0, 1, 2, 3, 4, 5]:
print i**2
for i in range(6):
print i**2
for i in xrange(6):
print i**2
-
Looping over a collection
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print colors[i]
for color in colors:
print color
-
Looping backwards
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)-1, -1, -1):
print colors[i]
for color in reversed(colors):
print color
-
Looping over collection and indicies
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print i, colors[i]
for i, color in enumerate(color):
print i, color
-
Looping over two collections at once
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
n = min(len(names), len(colors))
for i in range(n):
print names[i], "->", colors[i]
for name, color in zip(names, colors):
print name, "->", color
for name, color in izip(names, colors):
print name, "->", color
-
Looping over sorted order
colors = ['red', 'green', 'blue', 'yellow']
for color in sorted(colors):
print color
for color in sorted(colors, reverse=True):
print color
-
Custom sort order
colors = ['red', 'green', 'blue', 'yellow']
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 sentinal value. Note: Partial take a function with one or more arguments and returns a function with fewer argument.
blocks = []
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
blocks = []
for block in iter(partial(f.read, 32), ''):
blocks.append(block)
-
Distinguish multiple exit point in loops
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:
found = True
break
else:
return -1
return i
Dictionary Skills
- Mastering dictionaries is fundamental Python skill
- They are fundamental tool for expressing relationships, linking, counting and grouping
-
Looping over dictionary keys
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print k
```
- DONT mutate while iterating over things
-
Looping over dictionary keys and values
for k, v in d.items():
print k, "->", v
for k,v in d.iteritems():
print k, "->", v
```
-
Construct a dictionary in pairs
names = ['matthew', 'rachel', 'raymond']
colors = ['blue', 'green', 'raymond']
d = dict(izip(names, colors))
d = dict(enumerate(names))
```
-
Counting with dictionaries
colors = ['red', 'green', 'red', 'blue' 'green', 'red']
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
from collections import defaultdict
d = defaultdict(int)
for color in colors:
d[color] += 1
```
-
Grouping with dictionaries
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie']
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[name].append(key)
```
-
Is a dictionary popitem() atomic?
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
while d:
key, value = d.popitem()
print key, "->", value
```
-
Linking dictionaires (Or chaining them)
default = {}
d = defaults.copy()
d.update(os.environ)
d.update(command_line_args)
d.ChainMap(command_line_args, os.environ, defaults)
```
Improving Clarity
- Positional arguments and indicies are nice
- Keywords and names are better
- The first way is convinient for the computers
- The second corresponds to how human’s think
-
Clarify function calls with keyword arguments (Good activity for someone who doesn’t know the codebase)
twitter_search('@obama', False, 20, True)
twitter_search('@obama', retweets=False, numtweets=20, popular=True)
```
-
Clarify multiple return values with named tuples
doctest.testmod()
(0, 4)
doctest.testmod()
TestResults(failed=0, attempted=4)
TestResults = namedtuple('TestResult', ['failed', 'attempted'])
```
-
Unpacking Sequences
p = 'a', 'b', 'c', d
fname = p[0]
lname = p[1]
age = p[2]
email = p[3]
fname, lname, age, email = p
```
-
Update multiple state variables
def fibonacci(n):
x = 0
y = 1
for i in range(n):
print x
t = y
y = x + y
x = t
def fibonacci(n):
x, y = 0, 1
for i in range(n):
print x
x, y = y, x+y
```
Tuple packing and unpacking
- Don’t under-estimate the advantages of updating state variables at the same time
- It eliminates an entire class of errors due to out-of-order updates
- It allows high level thinking: “chunking”
Decorators and Context Managers
- Helps separate business logic and administrative logic
- Clean, beautiful tools for factoring code and imprving code reuse
- Good naming is essential
- Remember the Spiderman rule: With great power comes great responsibility
-
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
@cache
def web_lookup(url):
return urllib.urlopen(url).read()
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
```
-
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()
```
-
Factor-out temporary context
try:
os.remove('somefile.tmp')
except OSError:
pass
with ignored(OSError):
os.remove('somefile.tmp')
```
Python Outsourcing
Have a startup idea you’d like to see come to life? Have a legacy codebase that needs upgradation? Have a deadline and want to get job done on budget? Look no further, Hire Fafadia Tech
Why select Fafadia Tech?
- Rich experience working as remote team for 12+ Clients
- Clear communication & Excellent work ethic
- Capability to convert vauge requirements to working product
- Ability to solve complex problems