Useful Python code snippets and language properties

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

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 Trueand Falseconstants. 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.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.