TrashPanda Wiki

Editing

Python Style Guide

1
  • The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit. If you are undoing an edit that is …
Latest revision Your text
Line 1: Line 1:
== Common Python Idioms ==
+
=== Common Python Idioms ===
   
=== Unpacking ===
+
==== Unpacking ====
 
Multiple assignment (also known as tuple unpacking or iterable unpacking) allows you to assign multiple variables at the same time in one line of code.
 
Multiple assignment (also known as tuple unpacking or iterable unpacking) allows you to assign multiple variables at the same time in one line of code.
 
x, y = 10, 20
 
x, y = 10, 20
Line 21: Line 21:
   
 
a, *middle, c = [1, 2, 3, 4] # a = 1, middle = [2, 3], c = 4
 
a, *middle, c = [1, 2, 3, 4] # a = 1, middle = [2, 3], c = 4
===Create an Ignored Variable===
 
If you need to assign something (i.e in unpacking) but will not need that variable, use '''__ ''':
 
filename = 'file.txt'
 
basename, __, ext = filename.rpartition('.')
 
===Create a length-N list of the same thing===
 
Use the Python list '''* '''operator:
 
four_nones = [None] * 4
 
===Create a length-N list of lists===
 
Because lists are mutable, the '''* '''operator will create a list of N references to the <u>same</u> list, which is probably not what you want. Instead use a list comprehension:
 
four_lists = [[] for __ in xrange(4)]
 
===Create a String from a List===
 
a common idiom for creating strings is to use '''str.join() '''on an empty string:
 
letters = ['h', 'e', 'l', 'l', 'o']
 
word = ''''.join(letters)''
 
===Search for an item in a collection===
 
Sometimes we need to search through a collection of things. Here are two options: lists & sets:
 
s = set(['h', 'e', 'l', 'l', 'o'])
 
l = ['h', 'e', 'l', 'l', 'o']
 
 
def lookup_set(s):
 
return 'h' in s
 
 
def lookup_list(l):
 
return 's' in l
 
'''lookup_set '''is utilizing the fact that sets in Python are hashtables, therefore the lookup performance between the two is very different.
 
 
To determine whether an item is in a list, Python will have to go through each element until it finds a match. This can be time consuming.
 
 
In a set on the other hand, the hash of the item will tell Python where in the set to look for a matching item. As a result the search can be done quickly even in a large set. Searching in dictionaries works the same way. For more information see this [https://stackoverflow.com/questions/513882/python-list-vs-dict-for-look-up-table StackOverflow] page.
 
 
For detailed information on the amount of time various common operations take on each of these data structures, see [https://wiki.python.org/moin/TimeComplexity? this page].
 
 
Because of these differences in performance, it is often a good idea to '''use sets or dictionaries instead of lists''' in cases where:
 
*The collection will contain a large number of items.
 
*You will be repeatedly searching for items in the collection.
 
*You do not have duplicte items.
 
However if you will not be frequently searching through these items, the additional time and memory required to set up the hashtable will often be greater than the time saved by improved search speed.
 
===Zen of Python===
 
Also known as [https://www.python.org/dev/peps/pep-0020 PEP 20], the guiding principles of Python's design.
 
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!
 
===PEP 8===
 
[https://www.python.org/dev/peps/pep-0008 PEP 8] is the de facto code style guide for Python.
 
 
There is a command line program called '''[https://github.com/PyCQA/pycodestyle pycodestyle] '''(previously known as '''pep8''') that can check your code for conformance to PEP 8.
 
 
Install it by running:
 
pip install pycodestyle
 
Then run on a file or series of files to get a report on violations in your code:
 
pycodestyle file.py
 
'''[https://pypi.org/project/autopep8/ autopep8] '''can be used to automatically reformat code in the PEP 8 style. Install it by running:
 
pip install autopep8
 
You can use it to format a file in-place with:
 
autopep8 --in-place file.py
 
Running the '''--in-place '''flag will cause the program to output the modified code directly to the console for review.
 
 
The '''--aggressive '''flag will perform more substantial changes and can be applied multiple times for greater effect.
 
===Conventions===
 
Here are some conventions you should follow to make your code easier to read.
 
====Check if a variable equals a constant====
 
You don't need to explicitly compare a value to True or None or 0. You can just add it to the if statement.
 
 
'''BAD'''
 
if attr == True:
 
print 'True!'
 
if attr == None:
 
print 'attr is None!'
 
'''GOOD'''
 
# Just check the value
 
if attr:
 
print 'attr is truthey!'
 
 
# or check for the opposite
 
if not attr:
 
print 'attr is falsey!'
 
 
# or since None is considered false, explicitly check for it
 
if attr is None:
 
print 'attr is None!'
 
====Access a dicionary element====
 
Dont use the '''dict.has_key() '''method. Instead, use '''x in d '''syntax, or pass a default argument to '''dict.get()'''.
 
 
'''BAD'''
 
d = {'hello': 'world'}
 
if d.has_key('hello'):
 
print d['hello'] # prints 'world'
 
else:
 
print 'default_value'
 
'''GOOD'''
 
d = {'hello': 'world'}
 
 
print d.get('hello', 'default_value') # prints 'world'
 
print d.get('thingy', 'default_value') # prints 'default_value'
 
 
# OR:
 
if 'hello' in d:
 
print d['hello']
 
====Short ways to manipulate lists====
 
[http://docs.python.org/tutorial/datastructures.html#list-comprehensions List comprehension] provides a powerful, concise way to work with lists.
 
 
[http://docs.python.org/tutorial/classes.html#generator-expressions Greater expressions] follow almost the same syntax as list comprehension does but will return a greater list instead of a list.
 
 
Creating a new list requires more work and uses more memory. If you are jsut going to loop through the new list, prefer using an iterator instead.
 
 
'''BAD'''
 
# needlessly allocates a list of all (gpa, name) entries in memory.
 
valedictorian = max([student.gpa, student.name) for student in graduates])
 
'''GOOD'''
 
valedictorian = max((student.gpa, student.name) for student in graduates)
 
Use list conprehension when you really need to create a second list, for example if you need to use result multiple times.
 
 
If your logic is too complicated for a short list comprehension or generator expression, consider using a generator function instead of returning a list.
 
 
'''GOOD'''
 
def make_batches(items, batch_size):
 
"""
 
>>> list(make_batches([1, 2, 3, 4, 5], batch_size=3))
 
[[1, 2, 3], [4, 5]]
 
"""
 
current_batch = []
 
for item in items:
 
current_batch.append(item)
 
if len(current_batch) == batch_size:
 
yield current_batch
 
current_batch = []
 
yield current_batch
 
 
Never use a list comprehension just for its side effects.
 
 
'''BAD'''
 
 
[print(x) for x in sequence]
 
'''GOOD'''
 
for x in sequence:
 
print(x)
 
====Filtering a list====
 
'''BAD'''
 
 
Never remove items from a list while you are iterating through it.
 
# Filter elements greater than 4.
 
a = [3, 4, 5]
 
for i in a:
 
if i > 4:
 
a.remove(i)
 
Don't make multiple passes through the list.
 
while i in a:
 
a.remove(i)
 
'''GOOD'''
 
Use a list comprehension or generator expression.
 
# Comprehensions create a new list object.
 
filtered_values = [value for value in sequence if value != x]
 
 
# Generators don't create another list.
 
filtered_values = (value for value in sequence if value != x)
 
====Possible side effects of modifying the original list====
 
Modifying the original list can be risky if there are other variables referencing it. But you can use '''slice''' '''assignment '''if you really want to do that.
 
# replace the contents of the original list.
 
sequence[::] = [value for value in sequence if value != x]
 
====Modifying the values in a list====
 
'''BAD'''
 
 
Remember that assignment never creates a new object. If two or more variables refer to the same list, changing one of them changes all of them.
 
# Add three to all the list members.
 
a = [3, 4, 5]
 
b = a # a and b refer to the same list object.
 
 
for i in range(len(a)):
 
a[i] += 3 # b[i] also changes
 
 
'''GOOD'''
 
It's safer to create a new list object and leave the original alone.
 
a = [3, 4, 5]
 
b = a
 
 
# assign the variable "a" to a new list without changing "b".
 
a = [i + 3 for i in a]
 
Use '''enumerate() '''to keep count of your place in the list.
 
a = [3, 4, 5]
 
for i, item in enumerate(a):
 
print i, item
 
# prints:
 
# 0 3
 
# 1 4
 
# 2 5
 
The '''enumerate() '''funciton has better readability than handling a counter manually. Moreover, it is better optimized for iterators.
 
====Read from a file====
 
Use the '''with open''' syntax to read from files. This will automatically close files for you.
 
 
'''BAD'''
 
f = open('file.txt')
 
a = f.read()
 
print a
 
f.close()
 
'''GOOD'''
 
with open('file.txt' as f):
 
for line in f:
 
print line
 
====Line continuations====
 
When a logical line of code is longer that the accepted limit, you need to split it over multiple physical lines. The Python interpreter will join consecutive lines if the last character of the line is a backslash. This is helpful in some cases, but should usually be avoided because of its fragility:
 
 
a white space added to the end of the line, after the backslash, will break the code and may have unexpected results.
 
 
A better solution is to use parentheses around your elements. Left with an unclosed parentheses on an end-of-line the Python interpreter will join the next line until the parentheses are closed. The same behavior holds for curly and square braces.
 
 
'''BAD'''
 
my_very_big_string = """For a long time I used to go to bed early. Sometimes, \
 
when I had put out my candle, my eyes would close so quickly that I had not even \
 
time to say “I’m going to sleep.”"""
 
 
from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
 
yet_another_nice_function
 
'''GOOD'''
 
my_very_big_string = (
 
"For a long time I used to go to bed early. Sometimes, "
 
"when I had put out my candle, my eyes would close so quickly "
 
"that I had not even time to say “I’m going to sleep.”"
 
)
 
 
from some.deep.module.inside.a.module import (
 
a_nice_function, another_nice_function, yet_another_nice_function)
 
However, more often and not, having to split a long logical line is a sign that you are trying to do too many things as the same time, which may hinder readability.
 
===References===
 
*https://docs.python-guide.org/writing/style/
 
*https://github.com/hblanks/zen-of-python-by-example
 
*https://pep8.org/
 
*https://docs.python.org/3/library/stdtypes.html#truth-value-testing
 
  Loading editor
Below are some commonly used wiki markup codes. Simply click on what you want to use and it will appear in the edit box above.

View this template