COMP 200 Resources

[Rice University]

Python Tips and Traps

Common pitfalls and advice on how to avoid them!

Quick Links:

One Element Tuple

There is no such thing in Python as a one-element tuple!?!?

Suppose you wrote the following:

oneEltList = [42]
oneEltTuple = (42)

Looks like you're trying to make a one-element list and a one-element tuple right?   No complaints from Python...yet.

Buit wat!  Now try to loop over what you just made:

for x in oneEltList:
	print x
for y in oneEltTuple:
	print y

The one-element list prints just fine; a single "42" appears on the screen.    But not the one-element tuple!   You get a weird error message like the following:

It turns out that in Python, a one-element tuple is defined to be the element itself, that is, oneEltTuple is not a tuple, it is an integer, 42!   The error message is referring to the fact that the for-loop is expecting a collection of values that it can iterate over, but it got a single integer instead.


Copying Lists and Dictionaries

Try the following experiment:

aList1 = ['a', 'b', 'c']
aList2 = aList1
aList2[1] = 'z'
print aList1   # Why did aList1 change?!?!?! 

The reason is that aList1 and aList2 aren't really the lists themselves.   They are simply references to the lists, kind of like name tags.    In the above code snippet, there is only one actual list to which, initially, the name "aList1" referred.    The assignment, "aList2 = aList1" simply copied the reference to the list from one name tag to another--in the end, both aList1 and aList2 refer to the same list object.    Thus if you mutate (change) the list object via one reference, the other reference will see those changes.

The same problem arises for dictionaries and any other compound object made of smaller entities.

The solution?   You need to explicitly make a copy of the list or dictionary:

# For lists:

aList1 = ['a', 'b', 'c']
aList2 = aList1[:]  # use an unbounded slice, or
aList2 = list(aList)  # use a "copy constructor"

aList2[1] = 'z'   # test it!
print aList1   # aList1 is unchanged!

# For dictionaries:

aDict1 = {'a':1, 'b':2, 'c':3}

aDict2 = aDict1.copy()  # use the copy() method of a dictionary, or
aDict2 = dict(aDict1)  # use a copy constructor


Important Note:   ints, floats, booleans and chars are different!   

It should be noted that integers, floats, booleans and characters behave differently than the above discussed behavior of lists and dictionaries.    

anInt1 = 42
anInt2 = anInt1
anInt2 = 99
print anInt1   # anInt1 is unchanged

Integers, floats, booleans and characters are not compound objects, they are "primitive" values.   A variable of a primitive type is not a reference to an object, but rather the actual primitive entity.    Assignment actually does the same thing for both primitive and compound entities:  it copies the value of the variable itself, which for compound objects is the reference to the object while for primitives, it is the actual value of the entity.

Inconsistent Behavior Between regular lists and numpy arrays

 Many Python libraries use numpy arrays rather than regular lists for speed, efficiency and type safety.   One can easily convert a regular list into a one-dimensional numpy array:

import numpy as np

aNPArray = np.array(aList)

np.array([1, 2, 3]) #  displays as 'array([1, 2, 3])' 

For the most part, a one-dimensional numpy array and a list behave identically and you can usually use them interchangeably.    However, there are couple of "gotcha's":

# Both lists have same number of elements
[1, 2] + [3, 4]  # [1, 2, 3, 4]  -- plus sign means list concatenation
np.array([1, 2]) + np.array([3, 4])  # array([4, 6]) -- plus sign means element by element addition
[1, 2] - [3, 4]  # Error!  Subtraction not defined.
np.array([1, 2]) - np.array([3, 4])  # array([-2, -2]) -- minus sign means element by element subtraction

# One list has only one element:
[2] + [3, 4]  # [2, 3, 4]  -- plus sign means list concatenation as before
np.array([2]) + np.array([3, 4])  # array([4, 6]) -- plus sign here means to add the one element to all elements of other list
[2] - [3, 4]  # Error!  Subtraction not defined.
np.array([2]) - np.array([3, 4])  # array([-2, -2]) -- minus sign here means subtract every element from the second list from the one element of the first

# Lists have different numbers of elements
[1, 2] + [3, 4, 5]  # [1, 2, 3, 4, 5]  -- plus sign means list concatenation as usual
np.array([1, 2]) + np.array([3, 4, 5])  # Error!  Addition not defined for unequal lists.
[1, 2] - [3, 4]  # Error!  Subtraction not defined as usual.
np.array([1, 2]) - np.array([3, 4])  # array([-2, -2]) # Error!  Addition not defined for unequal lists.

Multiplication and division aren't defined for regular lists.    For numpy arrays, multiplication and division work similarly to plus and minus as shown above.