name = '2017-10-30-pythonic-code'
title = 'Writing Pythonic code'
tags = 'basics'
author = 'Denis Sergeev'
from nb_tools import connect_notebook_to_post
from IPython.core.display import HTML
html = connect_notebook_to_post(name, title, tags, author)
import this
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
import my_module_name
from foo import bar
# constant, not changed at runtime
MODEL_GRID = 'cartesian'
def calculate_something(data):
"""Compute some parameters"""
# ...
return result
class ModelDomain(object):
"""
Very long description...
Attributes: ...
Parameters: ...
"""
def __init__(self, name='', description='', bounds=None, mask_bounds=None):
self.name = name
self.description = description
# something else...
def mask_var(self, data):
"""Mask the data of the given variable outside the region."""
return data.where(self.mask_bounds, data)
class MyDescriptiveError(Exception):
pass
if []:
print('this is false')
False # false is false
[] # empty lists
{} # empty dictionaries or sets
"" # empty strings
0 # zero integers
0.00000 # zero floats
None # None (but not zero)
Thuthiness is defined by __bool__()
method
class MyClass:
def __init__(self, data):
self.data = data
def __bool__(self):
if len(self.data) > 0:
return True
else:
return False
foo = MyClass(data=[1, 2, 3])
if foo:
print('it contains some data')
else:
print('no data')
it contains some data
What's the pythonic way?
vrbl = True
if vrbl:
# NO: if condtion == True
print('do something')
do something
Don't be this guy:
def test_if_greater_than_ten(x):
return True if x>10 else False
test_if_greater_than_ten(11)
True
def fun():
print('blah')
x = fun
x()
blah
type(x)
function
callable(x)
True
isinstance(12345, (int, float))
True
None
¶if something is None:
print('no results')
else:
print('here are some results')
negation:
if something is not None:
print('Option A')
else:
print('Option B')
my_axis = 'x'
if my_axis in ('x', 'y'):
# Instead of writing like that:
# if my_axis == 'x' or my_axis == 'y'
print('horizontal')
elif my_axis == 'z':
print('vertical')
horizontal
Sometimes can be a bit slower if used inside a long loop.
a = [1,2,3]
How to check if the variable is a list or dictionary?
import numpy as np
a = np.arange(10)
isinstance(a, np.ndarray)
True
the_variable = [1, 2, 3, 4]
another_variable = "This is my string. There are many like it, but this one is mine."
and_another_variable = 1000000000000
for i in another_variable[:10]:
# iterate over the first 10 elements and print them
print(i)
T h i s i s m y
import collections
if isinstance(1234563645, collections.Iterable):
# iterable
print('It is iterable')
else:
# not iterable
print('It is NOT iterable')
It is NOT iterable
Similar way, by checking the methods:
hasattr(the_variable, '__iter__')
True
Another way: duck-typing style
Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.
try:
for i in the_variable:
# ...
except TypeError:
print(the_variable, 'is not iterable')
day = 30
month = 'October'
won't work, because Python does not automatically convert to str:
print('Today is ' + month + ', ' + day)
Works, but not pythonic:
print('Today is ' + month + ', ' + str(day))
Today is October, 30
print('Today is {}, {}'.format(month, day))
Today is October, 30
print('Today is {1}, {0}'.format(month, day))
Today is 30, October
print('Today is {1}, {0}. Tomorrow will be still {0}'.format(month, day))
Today is 30, October. Tomorrow will be still October
print('Today is {m}, {d}. Tomorrow will be still {m}. And again: {d}'.format(m=month, d=day))
Today is October, 30. Tomorrow will be still October. And again: 30
data = {'dow': 'Monday', 'location': 'UEA', 'who': 'Python Group'}
print('Today is {dow}. We are at {location}.'.format(**data))
Today is Monday. We are at UEA.
Just pulling variables from the namespace!
print(f'Today is {day}th. The month is {month}')
Today is 30th. The month is October
# print(f'Today is {data["dow"]}. We are at {data["location"]}')
HTML(html)
This post was written as an IPython (Jupyter) notebook. You can view or download it using nbviewer.