Collection of Python notes from experience, Stack Overflow, documentation, and also informed by the following very useful sources:
- Code Like a Pythonista: Idiomatic Python
- Transforming Code Into Beautiful Idiomatic Python
- Primer on Python Decorators
- Python Style Guide
We can use the queue module to implement a priority queue, the PriorityQueue class typically receives data in tuples of the form: (priority number, data). It also supports the methods empty(), qsize(), get(), and put().
import queue as Q
q = Q.PriorityQueue()
q.put((10, 'ten'))
q.put((1, 'one'))
q.put((5, 'five'))
while not q.empty():
print q.get()
# (1, 'one') (5, 'five') (10, 'ten')Trick to implementing this as a max priority queue is to add a negative sign to the priority numbers.
Pretty print makes inspecting data structures much easier, while the locals() function returns a dictionary of all locally-available names.
from pprint import pprint
pprint(locals())Decorators provide a simple syntax for calling higher-order functions. It is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
It is commonly used for authentication routes or rate limiting (as in the example below):
from time import sleep
def sleep_decorator(function):
"""
Limits how fast the function is called.
"""
def wrapper(*args, **kwargs):
sleep(2)
return function(*args, **kwargs)
return wrapper
@sleep_decorator
def print_number(num)
return numInstead of initializing dictionary entries before use (i.e. set a property to 0 if it does not exist, incrementing it otherwise), we can use dict.get(key, default).
navs = {}
for (portfolio, equity, position) in data:
navs[portfolio] = (navs.get(portfolio, 0)
+ position * prices[equity])For initializing objects within a dictionary, we can use the setdefault method to "set if necessary, then get". For example:
equities = {}
for (portfolio, equity) in data:
equities.setdefault(portfolio, []).append(equity)We can use dict and zip to build dictionaries from two lists (or sequences): one list of keys, and another list of values. For example:
given = ['John', 'Eric', 'Terry', 'Michael']
family = ['Cleese', 'Idle', 'Gilliam', 'Palin']
name_list = dict(zip(given, family))
# Can be reversed using the following
name_list.keys()
name_list.values()Files support a next method, as do other iterators (such as lists, tuples, dictionaries (for their keys), and generators.
datafile = open('datafile')
for line in datafile:
do_something(line)An even better approach might be:
with open('data.txt') as f:
data = f.read()The "args" and "kwargs" part of the declaration are just there by convention, the real syntax is in the choices to use * and **.
Use *args when you are not sure how many arguments might be passed to your function, and similarly **kwargs is used to handle named arguments that have not been defined in advance. For example:
def foo(required_argument, *args, **kwargs):
print(required_argument)
print(args)
print(kwargs)
foo('required', 'a', 'b', 'c', 'd', named='hello', word='world')
# required argument
# ('a', 'b', 'c', 'd')
# {'named': 'hello', 'word': 'world'}Note that * and ** can also be used when calling a function, e.g. to unpack a list (or tuple) of items. For example:
def print_three_things(a, b, c):
print ('a = %s, b = %s, c = %s' % (a, b, c))
random_list = ['testing', 'printing', 'three']
print_three_things(*random_list)
# a = 'testing', b = 'printing', c = 'three'List comprehensions allow for clear and concise syntax for list generation.
# Traditional way
new_list = []
for item in a_list:
if condition(item):
new_list.append(fn(item))
# List comprehension
new_list = [fn(item) for item in a_list if condition(item)]An optimal argument to the sort list method is key, which specifies a function of one argument that is used to compute a comparison key from each list element. For example:
def my_key(item):
return (item[1], item[3])
to_sort.sort(key=my_key)The function my_key will be called once for each item in the to_sort list, and there are many existing functions that can be used:
str.lowerto sort alphabetically regardless of caselento sort on the length of the itemsintorfloatto sort numerically, as with numeric strings like '2', '123', 35'
If updating sequences, ensure that you are using the right data structure. The deque data structure in Python is a generalization of stacks and queues (pronounced "deck" and short for "double-ended queue") that support efficient appends and pops from either side of the deque with approximately O(1) performance. For example, instead of:
names = ['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie']
del names[0]
names.pop(0)
names.insert(0, 'mark')We can use the deque data structure (which also has a clear() method):
names = deque(['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie'])
del names[0]
names.popleft()
names.appendleft('mark')To make a simultaneously importable module and exportable script:
if __name__ == '__main__':
# script code hereWhen imported, a module's __name__ attribute is set to the module's file name without the ".py". So the code guarded by the if statement will not run when imported. When executed as a script though, the __name__ attribute is set to "main", and the script code will run.
We can format strings without worrying about matching the interpolation values to the template using advanced string formatting. For example:
values = {'name': name, 'messages': messages}
print('Hello %(name)s, you have %(messages)i '
'messages' % locals())Use parentheses around elements, Python will join the next line until parentheses are closed. This works well for splitting multi-line logic, but also for long strings.
my_long_string = (
"Some very long line..."
"Another very long line..."
"Will all be concatenated nicely."
)