Up: Python

Aliasing

slide 001 Hello, and welcome to the seventh episode of the Software Carpentry lecture on Python. In this episode, we’ll take a break from introducing new features of the language, and talk a bit about what happens when one piece of data has two or more names.
slide 002 An alias is a a second name for a piece of data.
slide 003 Programmers create aliases because it’s often easier (or more useful) to have a second way to refer to data than to copy it.
slide 004 If the data in question is immutable—i.e., if it cannot be modified in place—then aliasing doesn’t matter…
slide 005 …because if the data can’t change, it doesn’t make a difference how many times it’s referred to.
slide 006 But if data can change in place, then aliasing can lead to some hard-to-find bugs.
slide 007 In Python, aliasing happens whenever one variable’s value is assigned to another variable, because variables are just names that store references to values.
slide 008 For example, if first refers to the string 'isaac'
slide 009 …then second = first copies the reference in first to second, after which second refers to the same string in memory as first.
slide 010 This can’t cause problems, because as we’ve already seen…
slide 011 …whenever we appear to change a string, Python actually creates a new string behind the scenes.
slide 012 However, this doesn’t happen with lists: they can be changed in place.
slide 013 Let’s assign a list containing the string 'isaac' to the variable first
slide 014 and then assign first to second. The two variables are now referring to the same thing in memory as before.
slide 015 If we now append another string to the list that first is pointing at…
slide 016 …the change is also visible when we look at second‘s value, because it’s the same value.
slide 017 We didn’t explicitly modify second—there’s nothing in the expression first.append('newton') to indicate that the value of second will change—but the change happens nonetheless.
slide 018 This is called a side effect, and as we said at the start of this episode, side effects can lead to some hard-to-find bugs.
slide 019 As an example, let’s look at how we might use lists of lists to implement a two-dimensional grid.
slide 020 A single outer list serves as the “spine” of the structure, while each row of values is stored in a sublist.
slide 021 If the variable grid refers to the outer list…
slide 022 …then grid[0] selects the first sublist…
slide 023 …and grid[0][1] selects the sublist’s second element. This lists-of-lists therefore gives us the two-dimensional indexing we want.
slide 024 Here’s some code to create an N×N grid of 1′s.
slide 025 The first statement creates the outer list—the “spine” of the structure.
slide 026 Each of the N iterations of the main loop adds another row to this.
slide 027 Inside the outer loop, another loop creates a sublist of N 1′s to be appended to the outer list.
slide 028 Here’s the same code with a small optimization:
slide 029 instead of temporarily storing the sublist being created in a variable called temp, this code just appends an empty sublist to the outer spine and then starts filling it in place, using grid[-1] to refer to it.
slide 030 Here’s another version that looks almost the same, but which contains a bug due to aliasing.
slide 031 If we highlight the changes, you can see that the buggy code is just giving the empty list a name.
slide 032 How can this be a bug? Aren’t meaningful variable names supposed to be a good thing?
slide 033 To see what’s going on, let’s watch this code execute. At the start of the outer loop, grid and EMPTY both refer to empty lists.
slide 034 The first thing the loop does is append the list pointed to by EMPTY to the outer list.
slide 035 When the inner loop runs for the first time…
slide 036 …it appends a 1 to that inner list.
slide 037 After two more iterations, our structure looks like this.
slide 038 Assuming N is 3, the program now goes back around the outer loop, and appends EMPTY to grid again.
slide 039 Whoops: we’ve just created an alias for our sublist of three 1′s, rather than appending a new empty list for filling in.
slide 040 The root of the problem is that empty square brackets always means a new empty list.
slide 041 But if we assign one variable’s value to another variable, we’re telling Python to create an alias for whatever the first variable was pointing at.
slide 042 Let’s try that again without the variable EMPTY.
slide 043 We append a new empty list…
slide 044 …then fill that sublist with 1s.
slide 045 On the second pass through the outer loop, we append a new empty sublist…
slide 046 …which we can then start filling. This time, everything is as we want it.
slide 047 So if aliasing can cause bugs, why does Python allow it?
slide 048 The first answer is that some languages don’t…
slide 049 …or at least go to great lengths to make it appear as if they don’t.
slide 050 Python, like C++ and Java, does allow aliasing because having multiple references to a million-element list is a lot more efficient than copying it over and over again.
slide 051 And sometimes we really do want to update structures in place via different routes.
slide 052 We’ll see examples of that in the next episode.

  1. luis
    January 27th, 2011 at 14:31 | #1

    There is an error in slides 15-18. Instead of “first = first.append(‘newton’)”, they should read “first.append(‘newton’)”, without the assignment.

    Luis.

  2. Dave
    September 27th, 2011 at 17:45 | #2

    Great presentation on aliasing! This is one of the more difficult aspects of Python for me to get my head around, but the lecture was very clear. I’d only suggest one addition, and that is a mention of copy.deepcopy for dictionary types.

  1. April 23rd, 2012 at 16:09 | #1