In this article I want to share a few code Python snippets that can help writing short and efficient code. I tested it with Python 3.5.2 on Ubuntu 16.04.
I will keep updating this article.
Contents:
- Check list for duplicate entries
- Revert list with array slicing
- Insert elements to lists at arbitrary positions using array slicing
- Python stores lists as references
- Print tables using Python’s string format function
- Having fun with Python 2 and True/False
Check list for duplicate entries
If you want to check if a list contains any element more than once you can compare its length to the length of the set representation of that list. Converting a list to a set automatically removes duplicates. So if the list contains duplicates, the set of the list will have less elements.
def has_duplicates(_list): return len(_list) != len(set(_list)) print(has_duplicates([1,2])) # False print(has_duplicates([2,2])) # True
Revert list (e.g. string) with array slicing
Reverting any list in python can be done by this:
elements = [1,2,3] reverted = elements[::-1] # [3,2,1]
[::-1] is the slice operator [a:b:c] which extracts all elements from index a to index b by using stepwidth c. As we do not provide a and b, the whole list is extracted. By setting c = -1 we start at the end and go to the start, so the result is the reverted list.
You can also easily check if a string is a palindrome by using this operator:
def is_palindrome(item): return item == item[::-1] print(is_palindrome("anna")) # True print(is_palindrome("python")) # False
Insert element(s) to lists at arbitrary positions using array slicing
Python’s array slicing operator [::] is extremely powerful. It does not only allow you to read/extract arbitrary elements of lists, it also supports list manipulation:
a = [1,2,3] # Prepend element to a list (you can also prepend multiple elements this way) a[:0] = [0] print(a) # [0, 1, 2, 3] # Mirror the list (generate palindrome) a[-1:] = a[::-1] print(a) # [0, 1, 2, 3, 2, 1, 0] # remove elements 2 to 4 (5 is not included) a[2:5] = [] print(a) # [0, 1, 1, 0] # Insert element at position 1 (you could also insert multiple elements here) a[1:1] = [99] print(a) # [0, 99, 1, 1, 0] # Replace all elements from position 1 to the end with the elements in range 1 to 2 (3 is not included) # Note that range(1,3) is a generator which gets converted to a list automatically a[1:] = range(1,3) print(a) # [0, 1, 2] # remove all elements except the last a[:-1] = [] print(a) # [2] # prepend multiple elements to the list a[:0] = [1,2,3,4,5,6] print(a) # [1, 2, 3, 4, 5, 6, 2] # remove last element (2) a[-1:] = [] print(a) # [1, 2, 3, 4, 5, 6] # Only revert every second element of the list (1,3,5 becomes 5,3,1) a[::2] = a[::2][::-1] print(a) # [5, 2, 3, 4, 1, 6]
These features can save lots of time. If you know other python shortcuts not covered in this article, please feel free to write a comment.
Python stores lists as references
When you add a list to another list and then manipulate the original list, the “copy” in the other list will also get manipulated:
a = [1,2,3] b = [] b.append(a) print(b) # [1,2,3] a[1] = 5 print(b) # [[1,5,3]]
If you want to make a real copy of a list, you can either transform it via list(a) or a[:]
a = [1,2,3] b = [] b.append(a) b.append(a[:]) b.append(list(a)) print(b) # [[1, 2, 3], [1, 2, 3], [1, 2, 3]] a[1] = 5 print(b) # [[1, 5, 3], [1, 2, 3], [1, 2, 3]]
You can see that the list appended to another list also gets manipulated when not using a[:] or list(a), but not if you use one of these. a[:] is the slicing operator with no start and end specified, so it will just copy all elements in the original order. But be careful: If a also contains just references they get copied, too. If you want to get a deep copy, you have to use copy.deepcopy(a) which resolves all references.
I think python uses references instead of copies by default in order to save memory
Print tables using Python’s string format function
I wrote a small helper function that enables me to print tables with the column width automatically calculated:
def print_table_row(data, maxlengths, padding, alignment): print("|".join(['{0:{align}{len}}'.format(data[i], len=maxlengths[i]+padding, align=alignment[i]) for i in range(len(data))])) def print_table(header, data, padding=3, alignment=None, line_every=-1): columns = len(header) if not alignment: alignment = ["^"] * columns lenghts = [[len(item[i]) for i in range(columns)] for item in data] maxlengths = [len(h) for h in header] for clength in lenghts: for i in range(columns): maxlengths[i] = max(maxlengths[i], clength[i]) print_table_row(header, maxlengths, padding, alignment) sepline = "-"*(sum(maxlengths) + padding * columns) print(sepline) for i, item in enumerate(data): print_table_row(item, maxlengths, padding, alignment) if line_every > 0 and (i+1)%line_every == 0 and i != len(data)-1: print(sepline) items = [["This", "is"], ["a", "test"], ["Python", "strings"], ["are", "awesome"]] print_table(["Column A", "Column B"], items, 10)
The output is:
Column A | Column B ------------------------------------ This | is a | test Python | strings are | awesome
In print_table_row() I use python’s extremely useful string format function. With this function you can align strings inside a given space, e.g. align a 5 character string in the space of 10 characters in the middle, on the left or on the right of the specified space. To print a table, I iterate over all elements of the table to determine the maximum width of each column and use that as the input for the string format function. You can also specify a padding in order to get more readable tables.
Having fun with Python 2 and True/False
In Python 2, you can change the values of the True
and False
constants. Look at this example:
>>> True True >>> False False >>> True, False = False, True # Swap True and False >>> True False >>> False True
This does only change the values that are assigned to the True/False constants. If you have conditions that are true or false, they will still work correctly, unless you use the True/False constants in them. Example:
>>> "still works" if 1==1 else "does not work" 'still works' >>> "still works" if (1==1) is True else "does not work" 'does not work' #because (1==1) is True does now effectively mean (1==1) is False, which is false. >>> 1 == 1 True
The old values can be restored in multiple ways. Suppose you have overridden True
with False
, then you can use different ways to restore the old value:
True = not False
True = not True
(as True is False)del True
(as True is False)True = __builtins__.True
True = bool(1)
- … and there are endless more possibilities
The del True
and True = __builtins__.True
tricks do not work if you have overridden __builtins__.True
instead of just True
.
I do not see any practical reason to use this kind of manipulation, other than confusing the readers of your code. Maybe that’s also the reason why this is not possible with Python 3 anymore. If you try to assign True = False
, you get:
SyntaxError: can’t assign to keyword
This stackoverflow question gives more information on how you can play with these constants.