A Few Gotchas for Python Beginners

Here are a few ‘gotchas’ for Python beginners to be aware of, in order to avoid simple errors that may not raise exceptions, and thus are difficult to detect.

Be aware that Python uses row-major order, just like C.

This is particularly important when dealing with NumPy arrays and the like. Read the article on row-major order and column-major order. Note that these are also called C-order and Fortran-order, respectively. Also note the lexicographical order, which is also demonstrated in the row-major order and column-major order article. In particular, note that the indices for the Cartesian coordinates (x, y) in a NumPy array are reversed, as in array[y, x] (the y is first).

Be aware of 0-based indexing versus 1-based indexing.

Python, like C, uses zero-based indexing. It’s actually quite useful. Read more here.

(For astronomers) be aware of the image layout convention

Most software projects that use a 2D pixel array to represent an image, use a convention which places the origin pixel (0, 0) at the upper-left corner of the image. Positive y points downward. Notable exceptions are OpenGL and FITS, which place the origin pixel at the lower-left corner. Matplotlib uses the more common convention, but in astronomy, we simply pretend that the y-coordinate increases upwards. Then, when we display the image, we flip it. In Matplotlib, you do this by specifying that the origin is at the bottom:

import matplotlib.pyplot as plt

### do some work that involves creating an image array, then plot it like this:

plt.imshow(image, origin='lower')

(For astronomers) Pixel Centroids vs. Pixel Edges

Matplotlib, the modern FITS standard, and many other software packages use a convention in which the centers of pixels correspond to integer values; the edges of pixels are half-integers. This convention is sometimes called the ‘pixel-centered’, ‘cell-centered’, or ‘face-centered’ convention. For example, in Matplotlib (and HTML5, etc.) the pixel coordinates (0, 0) correspond to the center of the upper-left pixel. The left edge of that pixel is at x = -0.5 and the right edge of the pixel is at 0.5. Be aware of the fact that some older FITS libraries and WCS libraries use the convention in which the edges of pixels are integers. If you exclusively use AstroPy for handling FITS, then you don’t have to worry about this, but if you use other software libraries, you need to verify that you know what convention is being used by those libraries.

References in Python

By default, variables for class instances (AKA ‘objects’) are simply references to the object. They are just different names for the same object in memory. This is a bit similar to pointers in C or hard links in a UNIX-like filesystem. Here’s a common scenario:

import numpy as np

a = np.array([1, 2, 3, 4])

b = a

# b is now a reference to the same object that a references. 
# If we change a, then b also changes:

a *= 2

# a is now [2, 4, 6, 8]

# b is also now [2, 4, 6, 8]

# in fact:

a is b

# returns True and:

id(a) == id(b)

# also returns True

Read about is and id in case you are not familiar with them.

If you need a and b to be independent copies, use copy.deepcopy

import copy

a = np.array([1, 2, 3, 4])

b = copy.deepcopy(a)

# b is now a copy of a

a *= 2

# a is now [2, 4, 6, 8]

# b is still [1, 2, 3, 4]


a is b

# returns False and:

id(a) == id(b)

# also returns False