-
-
Save ssukhawani/bbeab6599c843ba3d4c81f9e16d1d593 to your computer and use it in GitHub Desktop.
An extensive snippet-driven guide to writing idiomatic Python code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ****************************** | |
# * Quick guide to pythonicity * | |
# * by @csparpa * | |
# ****************************** | |
# === ZEN === | |
import this | |
# === HELP DOCSTRINGS === | |
help(range) | |
# === PYTHONIC KEYWORDS === | |
# The 'in' keyword. Used in for loops and to check if a value is contained into | |
# an iterable | |
3 in [4, 5, 6] # False | |
for c in "welcome": | |
print c | |
# The 'is' keyword is used to test for object identity | |
from datetime import datetime | |
a = datetime.today() | |
b = datetime.today() | |
b is a # Gives False, as a and b point to two distinct objects | |
c = a | |
c is a # Gives True, as both a and c point to the same object | |
# === TRUTHINESS === | |
# Different objects have different truthiness values. I.e: an empty containers, | |
# the integer 0 and None are considered False. | |
def truthiness(x): | |
return 'True' if x else 'False' | |
truthiness(None) # False | |
truthiness("") # False | |
truthiness([]) # False | |
truthiness({}) # False | |
truthiness(()) # False | |
truthiness("abc") # True | |
truthiness([1, 5]) # True | |
truthiness(0) # False | |
truthiness(1) # True | |
# Check for nonethiness explicitly, using the 'is' keyword | |
r = 3 | |
r is None # Gives False | |
r = None | |
r is None # Gives True | |
# === STRING FORMATTING === | |
# Use the '%' syntax... | |
'%s, %d, %f, %e' % ('a', 2, 34.6, 34.6) # Gives: 'a, 2, 34.600000, 3.460000e+01' | |
# ...or the newer '{} syntax' | |
'{0}, {1}, {2}'.format('a', 'b', 'c') # Gives: 'a, b, c' | |
'{2}, {1}, {0}'.format('a', 'b', 'c') # Gives: 'c, b, a' | |
'{2}, {1}, {0}'.format(*'abc') # Gives: 'c, b, a' | |
'{0}{1}{0}'.format('abra', 'cad') # Gives: 'abracadabra' | |
'{one}{two}'.format(one=1, two=2) # Gives: '12' | |
'{0}{1}'.format(*('x','y')) # Gives: 'xy' | |
'r={n.real} j={n.imag}'.format(n=2+3j) # Gives: 'r=2.0 j=3.0' | |
'{0[0]}{0[1]}{0[2]}'.format(['a','b','c']) # Gives: 'abc' | |
# Strings that span on multiple lines can be assigned with triple quotes | |
s="""my | |
very | |
long | |
phrase""" | |
print s # Gives: 'my\nvery\nlong\nphrase' | |
# Break very long strings into multiple lines while protecting newline insertion | |
s = 'this \ | |
really needs to \ | |
stay on a single line' | |
print s # Gives: 'this really needs to stay on a single line' | |
# Raw strings don't acknowledge escape sequences | |
print 'C:\\folder' # Prints: C:\folder | |
print r'C:\\folder' # Prints: C:\\folder | |
# === UNPACKING === | |
for index, item in enumerate(["a", "b", "c"]): | |
print "%d, %s" % (index, item) | |
# Use unpacking to swap contents of variables | |
a = 1 | |
b = 2 | |
a, b = b, a | |
# Function args unpacking: argument lists... | |
args = [3, 8] | |
range(*args) | |
# ... and dictionaries | |
def func(first, second=9, third=5): | |
print "first:%s, second:%s, third:%s" % (first, second, third) | |
func(1, **{'second': 'y', 'third': 'z'}) # Gives: first:1, second:y, third:z | |
func(**{'first': 'x', 'second': 'y', 'third': 'z'}) # Gives: first:x, second:y, third:z | |
# === SLICING === | |
# Extract sublists. Remember the '-1' offset when giving indexes! | |
l = [1, 2, 3, 4, 5] | |
l[:] # Gives [1, 2, 3, 4, 5] | |
l[0:0] # Gives [] | |
l[1:3] # Extracts items from index 1 to index 3-1=2, gives [2, 3] | |
l[:4] # Extracts items from index 0 up to index 4-1=3, gives [1, 2, 3, 4] | |
l[2:] # Extracts items from index 2 on, gives [3, 4, 5] | |
l[::2] # Extracts items with a step of 2 from index 0 on, gives [1, 3, 5] | |
l[2:4:2] # Extracts items with a step of 2 from index 2 to 4-1=3, gives [3] | |
# Also negative values can be provided to slicing: this means counting the item | |
# indexes from the end of the list on but backwards | |
l[::-1] # Reverses the list, gives [5, 4, 3, 2, 1] | |
l[-1] # Extracts the last element of the list, gives [5] | |
l[-4:-1] # Extracts items from index -4 to index -1-1=-2, gives [2, 3, 4] | |
# === COMPREHENSIONS === | |
# List comprehension | |
even_numbers = [n for n in range(1, 11) if n % 2 == 0] | |
evenness = [str(n) + ' is even' if n % 2 == 0 else str(n) + ' is odd' for n in range(1, 11)] | |
# Dictionary comprehension | |
odd_numbers = {n:'odd' for n in range(1, 11) if n % 2 != 0} | |
oddness = {n:('odd' if n%2 != 0 else 'even') for n in range(1, 11)} | |
list_to_dict = {index: value for index, value in enumerate(['a', 'b', 'c'])} | |
# Set comprehension | |
even_numbers = { x for x in range(1, 11) if x % 2 == 0} | |
# === BUILTINS === | |
# Range: generates a sequential list, which takes O(n) memory. | |
first_ten = range(1, 11) | |
even_numbers_under_ten = range(0, 10, 2) | |
first_ten_negatives = range(-1, -11, -1) | |
# XRange: returns a generator, which takes O(1) memory. | |
first_ten = xrange(1, 11) | |
word = 'welcometopython' | |
for i in first_ten: | |
print i | |
# All/any: | |
contains_empty_string = all(['hello', '', 'friends']) #False | |
contains_at_least_one_non_empty_string = any(['hello', '', 'friends']) #True | |
# Characters: chr, ord, unichr | |
chr(56) # Gives the 8-bit char with the specified ASCII code (gives '8') | |
ord('8') # Gives the Unicode code point for the specified 8-bit char (gives 56 again) | |
unichr(56) # Gives the Unicode char for the specified code point (gives u'8') | |
# String/Unicode representations | |
uc = unicode('abc^Y', 'utf-8') # Gives the Unicode representation for the specified | |
# object (gives u'abc\x19') | |
st = str(uc) # Gives the 8-bit string representation for the | |
# specified object | |
# Containers builders: dict, list, set, tuple | |
l=list() | |
l.append('a') | |
l.append('b') | |
l.append('a') | |
t=tuple(l) | |
s=set(t) | |
d=dict([(1, 'a'), (2, 'b')] | |
# Enumerate | |
for t in enumerate(['a', 'b', 'c', 'd'], 1): | |
print t | |
# Dir: lists attributes/names in the scope local to the specified object | |
dir() #Local scope names | |
dir(module_x) #Attributes in module_x's scope | |
# Filter: gives a list from those elements of iterable for which the filtering | |
# function returns True | |
f = lambda x: True if x % 2 != 0 else False #Extract odd numbers | |
filter(f, [1, 2, 3, 4, 5]) #Gives [1, 3, 5] | |
# Map: apply a function to every item of iterable and return the list of the results | |
f = lambda x: True if x % 2 != 0 else False #Extract odd numbers | |
map(f, [1, 2, 3, 4, 5]) #Gives [True, False, True, False, True] | |
# Reduce: apply a function of two args cumulatively to the items of iterable, from | |
# left to right, so as to reduce the iterable to a single value | |
reduce(lambda a, b: a*b, [1, 2, 3, 4, 5]) #Gives 120 | |
# Isinstance: gives True if object's class is of type/subtype of the one declared | |
t=(1, 2, 3) | |
t_class = t.__class__ | |
isinstance(t, t_class) #True | |
t=[1, 2, 3] | |
isinstance(t, t_class) #False | |
# Iter/Next: returns iterator of iterables and then retrieve the next element from it | |
i=iter([1, 2, 3, 4, 5]) | |
i.next() | |
i.next() | |
# Locals: returns a dict with the local scope variables | |
def f(a, b): | |
print locals() | |
f(4,5) # Gives {'a': 4, 'b': 5} | |
# Len | |
len('helloworld') # Gives: 10 | |
len(range(1, 100)) # Gives: 99 | |
# Sorted: returns a list of sorted items from the input iterable | |
sorted(('mad', 'it\'s', 'world', 'a')) #Gives ['a', "it's", 'mad', 'world'] | |
sorted([7, 2, -9, 0, 0, 2]) #Gives [-9, 0, 0, 2, 2, 7] | |
# Getattr/Setattr/Delattr/Hasattr | |
# Getattr performs a name-based lookup of objects attributes. Hasattr tells you | |
# if an object has the specified attribute. Setattr adds a named and valued | |
# attribute to the object or modifies one already existing with that name, while | |
# Delattr deletes an attribute from it | |
# Attribute addition/modification/deletion must be allowed by the objects you | |
# want to operate on! | |
import datetime | |
d = datetime.datetime(2010,3,4) | |
getattr(d, "month") # Gives: 3 | |
getattr(d, "ctime") # Gives 'ctime', which is a function | |
hasattr(d, "month") # Gives: True | |
setattr(d, "foobar", 123) # Gives an error | |
class foobar(): | |
pass | |
f = foobar() | |
setattr(f, "foobar", 123) # Gives: True | |
delattr(f, "foobar") | |
hasattr(f, "foobar") # Gives: False | |
# Type: gives the type of its argument | |
# Module 'types' has constants that you can use to compare types | |
type(5) # Gives: <type 'int'> | |
type('x') # Gives: <type 'str'> | |
type(len) # Gives: <type 'builtin_function_or_method'> | |
type(int) # Gives: <type 'type'> -> what??? See the "metaclasses" section | |
type('a') == types.StringType # Gives: True | |
type(len) == types.BuiltinMethodType # Gives: True | |
# Zip: gives a list of tuples, each one having as items the items at the same | |
# indexes of the sequences you passed in as arguments | |
zip([1, 4, 7],[2, 5, 8], [3, 6, 9]) # Gives [(1, 2, 3), (4, 5, 6), (7, 8, 9)] | |
zip({'x': 1, 'y': 2},['a','b'], (9,8)) # Gives [('y', 'a', 9), ('x', 'b', 8)] | |
# === FILE I/O === | |
# Specify a mode when you open a file: the deafault mode is 'r', use 'w' for | |
# writing text files, specify: 'rb', 'wb' for r/w binary files, use 'a' for | |
# appending. | |
# Use the 'with' keyword to ensure that file handle is closed also if errors | |
# are raised. If you don't, you'll need to explicitly call close() on the handle. | |
# Read a file: file-like objects are iterable | |
with open('file.txt') as f: | |
for line in f: # Alternatives are f.readline() and f.readlines() | |
print line | |
# Write a file | |
with open('file.txt', 'a') as f: | |
f.write('helloworld') | |
# === STRINGS === | |
#Strings join | |
line = ' '.join(['99', 'bottles', 'of', 'beer', 'on', 'the', 'wall']) | |
# === GENERATORS === | |
#Generator functions (aka "coroutines"): functions that can "save their work" and | |
#return execution control to their callers, expecting to regain it in the future. | |
#The keyword "yield" is used to freeze coroutines' execution state (inner vars) | |
#and execution resumes when next() is called | |
#Generator functions create "generator objects", which are special iterators and | |
#can therefore be used in for loops | |
def simple_generator(): #generator function | |
yield 1 | |
yield 2 | |
yield 3 | |
sg = simple_generator() | |
next(sg) | |
next(sg) | |
next(sg) | |
for i in simple_generator(): #loops autoatically call 'next()' on the generator | |
print i | |
#Infinite generators: eg, we want to get all the even numbers >= x | |
def even_numbers_from(x): | |
while 1: | |
if x % 2 == 0: | |
yield x | |
x += 1 | |
#Generator expressions | |
gen_exp = (n for n in range(1, 3)) | |
for i in gen_exp: | |
print i | |
# === FUNCTION ARGUMENTS === | |
#Positional args vs keyword args | |
def func1(a, b='x', c=None): | |
print "a:%s, b:%s, c:%s" % (a, b, c) | |
func1(1) | |
func1(1, 2) | |
func1(1, 2, 3) | |
#Arbitrary args list | |
def func2(a, b='x', *args): | |
print "a:%s, b:%s, c:%s" % (a, b, args) | |
func2(1) | |
func2(1, 2) | |
func2(1, 2, 'a') | |
func2(1, 2, 'a', 'b', 'c') | |
func2(1, 2, [1, 2, 3, 4], {'a': 1, 'b': 2}) | |
#Arbitrary args list + arbitrary args dict | |
def func3(a, b='x', *args, **kwargs): | |
print "a:%s, b:%s, c:%s, d:%s" % (a, b, args, kwargs) | |
print 'ciccio' | |
func3(1) | |
func3(1, 2) | |
func3(1, 2, 'a') | |
func3(1, 2, [1, 2, 3, 4], {'a': 1, 'b': 2}) | |
func3(1, 2, [1, 2, 3, 4], p={'a': 1, 'b': 2}) | |
func3(1, 2, [1, 2, 3, 4], p=1, q=2) | |
# === LAMBDAS === | |
#'lambda' is used to build anonymous function objects, good to be used only once | |
is_even = lambda x: True if x %2 == 0 else False | |
is_even(4) | |
is_even(5) | |
(lambda x: True if x%2!=0 else False)(5) #this tells you on the fly wether x is odd | |
# === CLASS PROPERTIES === | |
#dinamically attach attributes to classes, specifying getter/setter/deleter handles | |
class C(object): | |
def __init__(self): | |
self._x = None | |
def getx(self): | |
return self._x | |
def setx(self, value): | |
self._x = value | |
def delx(self): | |
del self._x | |
x = property(getx, setx, delx, "I'm the 'x' property.") | |
#Alternate way to implement properties: use decorators | |
class C(object): | |
def __init__(self): | |
self._x = None | |
@property | |
def x(self): | |
"""I'm the 'x' property.""" | |
return self._x | |
@x.setter | |
def x(self, value): | |
self._x = value | |
@x.deleter | |
def x(self): | |
del self._x | |
# === FUNCTION CLOSURES === | |
# In Python, everything is an object - functions are not alike. You have a | |
# function closure when inner functions defined in non-global scope remember | |
# what their enclosing namespaces looked like at definition time. | |
def outer(): | |
x = 1 | |
# 'inner' is defined each time that 'outer' is invoked | |
def inner(): | |
print x # The value of 'x' is remembered and printed when inner is executed | |
return inner | |
f = outer() # 'f' contains the 'inner' function | |
f() | |
# === FUNCTION DECORATORS === | |
# Without arguments | |
def decorator(func): | |
def decorated_function(n): | |
print 'I am the decorator' | |
return func(n) | |
return decorated_function | |
@decorator | |
def inner(n): | |
print 'I am the inner function' | |
print 'n is %d' % (n,) | |
inner(4) # Gives: I am the decorator | |
# I am the inner function | |
# n is 4 | |
# With arguments: we need a function (decorator factory) that takes | |
# the arguments and builds the actual generator | |
def greet(formula='hello'): # decorator factory | |
def decorator(func): # decorator | |
def decorated(*args, **kwargs): # decorated function | |
print '%s' % (formula,) | |
return func(*args, **kwargs) # original function | |
return decorated | |
return decorator | |
@greet(formula='God save you, o') | |
def spell(name): | |
print '%s' % (name,) | |
spell('Queen') # Prints: | |
# God save you, o | |
# Queen | |
# === CLASS DECORATORS === | |
def addAge(cls): | |
original_init = cls.__init__ | |
def __init__(self, age, *args, **kws): | |
self.__age = age | |
original_init(self, *args, **kws) | |
def get_age(self): | |
return self.__age | |
cls.__init__ = __init__ # we replace the original __init__ with the "decorating" one | |
cls.get_age = get_age # we bind the additional function to the class | |
return cls | |
@addAge | |
class Person(): | |
def __init__(self, name): | |
self.__name = name | |
def get_name(self): | |
return self.__name | |
p = Person(32, 'Claudio') # pay attention to params order! | |
print p.get_name(), p.get_age() | |
# === METACLASSES === | |
# In Python, every class (either built-in or user-defined) is of type 'type'. | |
# Metaclasses are "class-builders": they can be any callable that return a | |
# 'type'. If you define your own metaclass, you can build 'type' instances | |
# programmatically using the 'type(name, bases, dict)' builtin: | |
# E.g: this is a parent class | |
class MyParentType(object): | |
_x = 'parent' | |
# ... and this is the child class | |
class MyType(MyParentType): | |
_y = 'child' | |
def sum(a, b): | |
return a+b | |
# I want to be able to create the 'MyType' class also programmatically. I can do | |
# it like this: | |
mytype = type('MyType', # Name for the generated-class | |
(MyParentType,), # Base classes tuple | |
{'_y': 'child', 'sum': lambda self,a,b: a+b} #dict containing attributes and functions | |
) | |
# Now 'mytype' contains a fully-featured class definition and can be instantiated | |
print mytype # Gives: <class '__main__.MyType'> | |
obj = mytype() | |
obj._x # Gives: 'parent' | |
obj._y # Gives: 'child' | |
obj.sum(7,6) # Gives: 13 | |
# The __metaclass__ hook allows to specify the callable that is used to build the | |
# class object when the 'class' statement is executed. | |
# If you don't specify any value for __metaclass__, then the package's __metaclass__ | |
# is used - if even that one is undefined, Python looks into the __metaclass__ of | |
# parent classes up in the chain. If no matches are found, then 'type' is used. | |
def bind_increment_function(class_name, class_parents, class_attrs): | |
attrs = class_attrs.copy() | |
attrs.update({'_param': 'x'}) | |
return type(class_name, class_parents, attrs) | |
class Number(): | |
__metaclass__ = bind_increment_function # the metaclass callable | |
def __init__(self, n): | |
self._n = n | |
def add(self, number): | |
return self._n + number | |
o = Number(7) | |
o._n # Gives: 10 | |
o._param # Gives: 'x' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment