Understanding nested list comprehensions in Python

In my last blog post, I discussed list comprehensions, and how to think about them. Several people suggested (via e-mail, and in comments on the blog) that I should write a follow-up posting about nested list comprehensions.

I must admit that nested list comprehensions are something that I’ve shied away from for years. Every time I’ve tried to understand them, let alone teach them, I’ve found myself stumbling for words, without being clear about what was happening, what the syntax is, or where I would want to use them. I managed to use them on a few occasions, but only after a great deal of trial and error, and without really understanding what I was doing.

Fortunately, the requests that I received, asking how to work with such nested list comprehensions, forced me to get over my worries. I’ve figured out what’s going on, and even think that I understand what my problem was with understanding them before.

The key thing to remember is that in a list comprehension, we’re dealing with an iterable. So when I say:

[ len(line) 
for line in open('/etc/passwd') ]

I’m saying that I want to iterate over the file object we got from opening /etc/passwd. There will be one element in the output list for each element in the input iterable — aka, every line in the file.

That’s great if I want my list comprehension to return something based on each line of /etc/passwd. But each line of /etc/passwd is a string, and thus also iterable. Maybe I want to return something not based on the lines of the file, but on the characters of each line.

Were I to use a “for” loop to process the file, I would use a nested loop — i.e., one loop inside of the other, with the outer loop iterating over lines and the inner loop iterating over consonants. It turns out that we can use a nested list comprehension, too. Here’s a simple example of a nested list comprehension:

[(x,y) for x in range(5) for y in range(5)]

If your reaction to this is, “What in the blazes does that mean?!?” then you’re not alone. Until just recently, that’s what I thought, too.

However: If we rewrite the above nested list comprehension using my preferred (i.e., multi-line) list-comprehension style, I think that things become a bit clearer:

 [(x,y)  
 for x in range(5)  
 for y in range(5)]

Let’s take this apart:

  • Our output expression is the tuple (x,y). That is, this list comprehension will produce a list of two-element tuples.
  • We first run over the source range(5), giving x the values 0 through 4.
  • For each value in x, we run through the source range(5), giving y the values 0 through 4.
  • The number of values in the output depends on the number of runs of  the final (second) “for” line.
  • The output, not surprisingly, will be all of the two-element tuples from (0,0) to (4,4).

Now, let’s mix things up by changing them a bit:

 [(x,y)  
  for x in range(5)  
  for y in range(x+1)]

Notice that now, the maximum value of y will vary according to the value of x. So we’ll get from (0,0) to (4,4), but we won’t see such things as (2,4) because y will never be larger than x.

Again, it’s important to understand several things here:

  • Our “for y” loop will execute once for each iteration over x.
  • In our “for y” loop, we have access to the variable x.
  • In our “for x” loop, we don’t have access to y (unless you consider the last value of y to be useful, but you really shouldn’t).
  • Our (x,y) tuple is output once for each iteration of the *final* loop, at the bottom.

Here’s another example: Assume that we have a few friends over, and that we have decided to play several games of Scrabble. Being Python programmers, we have stored our scores in a dictionary:

{'Reuven':[300, 250, 350, 400], 
 'Atara':[200, 300, 450, 150], 
 'Shikma':[250, 380, 420, 120], 
 'Amotz':[100, 120, 150, 180] }

I want to know each player’s average score, so I write a little function:

def average(scores):  
    return sum(scores) / len(scores)

If we want to find out each individual’s average score, we can use our function and a standard comprehension — in this case, a dict comprehension, to preserve the names:

 >>> { name : average(score)  
       for name, score in scores.items() }

{'Amotz': 137, 'Atara': 275, 'Reuven': 325, 'Shikma': 292}

But what if I want to get the average score, across all of the players? In such a case, I will need to grab each of the scores from inside of the inner lists. To do that, I can use a nested list comprehension:

>>> average([ one_score  
              for one_player_scores in scores.values()  
              for one_score in one_player_scores ])

257

What if I’m only interested (for whatever reason) in including scores that were above 200? As with all list comprehensions, I can use the “if” clause to weed out values that I don’t want. That condition can use any and all of the values that I have picked out of the various “for” lines:

>>> [ one_score      
      for one_player_scores in scores.values()     
      for one_score in one_player_scores
      if one_score > 200]

[300, 250, 350, 400, 300, 450, 250, 380, 420]

If I want to put these above-200 scores into a CSV file of some sort, I could do the following:

>>> ','.join([ str(one_score)  
               for one_player_scores in scores.values() 
               for one_score in one_player_scores  
               if one_score > 200])

'300,250,350,400,300,450,250,380,420'

Here’s one final example that I hope will drive these points home: Let’s assume that I have information about a hotel. The hotel has stored its information in a Python list. The list contains lists (representing rooms), and each sublist contains one or more dictionaries (representing people). Here’s our data structure:

rooms = [[{'age': 14, 'hobby': 'horses', 'name': 'A'},  
          {'age': 12, 'hobby': 'piano', 'name': 'B'},  
          {'age': 9, 'hobby': 'chess', 'name': 'C'}],  
         [{'age': 15, 'hobby': 'programming', 'name': 'D'}, 
          {'age': 17, 'hobby': 'driving', 'name': 'E'}],  
         [{'age': 45, 'hobby': 'writing', 'name': 'F'},  
          {'age': 43, 'hobby': 'chess', 'name': 'G'}]]

What are the names of the people staying at our hotel?

 >>> [ person['name']      
       for room in rooms
       for person in room ]

['A', 'B', 'C', 'D', 'E', 'F', 'G']

How about the names of people staying in our hotel who enjoy chess?

>>> [ person['name']  
      for room in rooms  
      for person in room  
      if person['hobby'] == 'chess' ]

['C', 'G']

Basically, every “for” line flattens the items over which you’re iterating by one more level, gives you access to that level in both the output expression (i.e., first line) and in the condition (i.e., optional final line).

I hope that this helps you to understand nested list comprehensions. If it did, please let me know! (And if it didn’t, please let me know that, as well!)

Want to understand Python’s comprehensions? Think in Excel or SQL.

Comprehensions are among the most useful constructs in Python. They merge the old, trusty “map” and “filter” functions into a single piece of compact, elegant syntax, allowing us to expression complex ideas in a minimum of code. Comprehensions are one of the most important tools in a Pythonista’s toolbox.

And yet, I have found that a very large number of Python programmers, including some experienced developers, are not completely comfortable with comprehensions. There are two reasons for this: First, it’s not obvious when to use them, and what sorts of problems they solve. The second problem, which is at least as important, is that the syntax is hard for people to remember and understand.

I’ve started to use a new explanation and introduction to comprehensions in my Python classes, and have found that it helps to lower the learning curve to some degree. In this post, I’m publicizing this explanation, in the hopes that it’ll help Python developers to understand when, where, and how to use comprehensions.

Let’s take a simple problem: I want to take a list of five integers, and get a list of their squares. If you give this problem to a new (or even intermediate) Python programmer, the answer would look something like this:

numbers = range(5)
output = [ ]
for number in numbers:
    output.append(number * number)
print(output)

Now, the thing is that this does work. (In my courses, I often use the phrase, “Unfortunately, this works.”) Often, when I talk about comprehensions, I talk about functional programming, the idea of immutable data structures, the idea that we don’t want to change things, and the benefits of thinking in terms of mapreduce.

But let’s ignore all of that, and ask a simpler question: If you were to give this problem to your accountant, how would they solve the problem?

Almost certainly, an accountant would fire up Excel, and put the numbers in a column:

A
-
0
1
2
3
4

Let’s assume that the above numbers are in the spreadsheet’s column A. The Excel user would, given this task, then tell Excel that column B should be calculated as A*A. And it would be done:

A  B
-  -
0  0
1  1
2  4
3  9
4  16

You could argue that the difference here is that Excel has a GUI, and Python doesn’t. But that’s missing the point. The real difference is that our accountant told Excel how to transform the first column into the second column, whereas our Python developer wrote a program that describe how to carry out that transformation.

We can think about this in a different way, too: Rather than solving the problem serially, as in the above for loop, the accountant is thinking in a parallel manner, applying a single expression to a large data set. The Excel user doesn’t care, or even know, the order in which the numbers are handed to the expression. The important thing is that the expression is applied once to each of the numbers, and that the final result appears in the correct order.

We might laugh at Excel, and dismiss its users as technical neophytes. And certainly, many users of Excel would deny that they possess serious programming chops. But this sort of thinking, which is so fundamental and natural to Excel users, is alien to many programmers. Which is a shame, because it allows us to express a very large number of ideas in a simple way.

To summarize this approach:

  • Think of your input as an iterable source of data
  • Think of what operation you want to apply to each element of that source
  • Get a new sequence out

That’s what the traditional “map” function does. Python does have a “map” function, but today, we typically use list comprehensions instead.

Let’s try to make this a bit more concrete, using the example that I used above: Let’s say that we have a list of five numbers, and we want to turn that list into a list of its squares. The list-comprehension syntax looks as follows:

[number * number for number in range(5) ]

Yikes. No wonder people are scared off by this syntax.  Let’s take the above syntax apart:

  • First of all, we’re going to get a list back. (It’s called a “list comprehension” for a reason.) That’s because of the square brackets, which are mandatory, and which tell Python what sort of object to create.
  • The data source will be “range(5),” which returns a list.
  • Each element in the data source will be assigned, in turn, to the iteration variable “number.”
  • We’ll invoke the operation “number * number” on each element of the data source.

In other words, we’re creating a new list, the elements of which are the result of applying our expression to each element of the source. This sounds suspiciously like what our accountant did above, using Excel: We’re telling Python what we want, and how to transform our source to that result. But how are things done internally? How is the list created? We neither know nor care.

List-comprehension syntax can be daunting for people to understand, in part because the order of the operations seems unusual. I’ve found that it can help to rewrite list comprehensions in the following way:

[number * number
 for number in range(5) ]

Yes, that’s right — I now spread list comprehensions across two lines; the first describes the operation I want to invoke, and the second line describes the data source. If this still seems unfamiliar, let’s try to bring it into a context with which you might have some experience:

[number * number           # SELECT
 for number in range(5) ]  # FROM

While they’re not directly equivalent, there are a fair number of similarities between a SELECT query in SQL, the placement of its SELECT expression and FROM clause, and our list comprehension.  The FROM clause in an SQL query describes our data source, which is typically going to be a table, but can also be a view or even the result of a function call. And the initial part of the SELECT is often the name of a column, but  can include function calls and operators.

On the one hand, the SELECT-FROM combination seems almost too simple to mention, in that you’re just retrieving a selected set of values from a data source.  On the other hand, such queries form the backbone of the database industry. In the same way, such functionality forms the backbone of many Python programs, iterating over a data structure, and plucking out part of it, transforming that part, and then returning a new list.

One of my favorite examples (and an exercise in my ebook, “Practice Makes Python“) is to take the /etc/passwd file used in Unix, and get the usernames contained within that file. /etc/passwd consists of one record per line, and the fields are separated by colons. Here are several lines from the /etc/passwd on my computer:

nobody:*:-2:-2::0:0:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0::0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1::0:0:System Services:/var/root:/usr/bin/false
_uucp:*:4:4::0:0:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico

We might normally think of a file as a collection of bytes, to which we give semantic meaning when we read it. But in Python, we’re encouraged to see a file as an ordered, iterable collection of lines of text. True, I can read from a file based on bytes, but it’s so common to want to read files by line that the language provides several constructs to do so.

We know that we can iterate over the lines of a file:

for line in open('/etc/passwd'):
    print(line)

This demonstrates that a file is iterable, which means that it can serve as a data source for a list comprehension. This means that the above code can be rewritten as:

[line
 for line in open('/etc/passwd')]

Again, the first line in our list comprehension represents the expression we want to apply to every element of our data source. In this case, the expression is just the line.  If we want to get the username from each of  these lines, we just need to apply the “split” method on the string, returning a list — and then retrieve index 0 from the resulting list.  For example:

[line.split(":")[0]
 for line in open('/etc/passwd')]

Again, we can think of it in terms of an SQL query:

SELECT username
FROM users

But of course, “username” in the above is a column name.  A more equivalent query to my list comprehension would be a “Users” table with an “info” column, queried as follows:

SELECT split_part(info, ':', 1)
FROM users;

Note that in this case, I’m using the built-in PostgreSQLsplit_part” operator to perform the equivalent operation to the str.split method in Python.

Remember that in the case of my SQL query, the result of a query always looks and acts like a table. The number and types of columns returned will depend on the number and types of expressions that I have in the SELECT  statement.  But the result set will have one or more columns, and zero or more rows.

In the same way, the result of a list comprehension is always going to be a list.  You can have whatever expression you want inside of the list comprehension; the expression represents one item in a list, not the list itself.

For example, let’s assume that I want to turn the usernames in /etc/passwd into a list of dictionaries. This doesn’t require a dictionary comprehension, which creates a single dictionary.  Rather, it requires a list  comprehension, in which the expression creates a dictionary.  Here’s a simple-minded such list comprehension:

[ {'name':line.split(":")[0]}
   for line in open('/etc/passwd')]

The above will work, in that it creates a list of dictionaries. And each dictionary has a single key-value pair.  But it seems a bit silly to do the above.  Rather, I’d probably want to have a dictionary containing the username and the numeric user ID, which is at index 2. I can then write:

[ {'name':line.split(":")[0], 'id':line.split(":")[2]}
for line in open('/etc/passwd')]

Again, we can think about this in terms of Excel, or even in terms of SQL: My query now produces a single column of results, but each column contains a text string. Or we can even say that the query produces two columns of results, which is not at all unusual in the world of SQL.

Let’s ignore the efficiency (or lack thereof) of invoking str.split twice in one comprehension: When I run this code on my Mac, it results in an exception, claiming that an index is out of range.

The reason is simple: I split each line into a list. But if there’s a line that doesn’t contain any : characters, it’ll be turned into a single-element list. I thus need to weed out any lines that won’t conform. Specifically, on my Mac at least, I need to remove any lines in /etc/passwd that are comments, meaning that they start with the ‘#’ character.

In the world of list comprehensions, I say the following:

[ {'name':line.split(":")[0], 'id':line.split(":")[2]}
for line in open('/etc/passwd')
if not line.startswith("#")]

Let’s extend our earlier SQL analogy further, adding the equivalent SQL syntax in comments after our Python code:

[ {'name':line.split(":")[0], 'id':line.split(":")[2]}    # SELECT
for line in open('/etc/passwd')                           # FROM
if not line.startswith("#")]                              # WHERE

Of course, when the first line of our comprehension becomes this long, it’s often a good idea to use a function, instead. And since the first line can be any legitimate Python expression, a function is often a good idea:

def get_user_info(line):
    name, passwd, id, rest = line.split(":", 3)   # max 4 fields
    return {'name':name, 'id':id}

[ get_user_info(line)             # SELECT
for line in open('/etc/passwd')   # FROM
if not line.startswith("#")]      # WHERE

A list comprehension thus gives you power similar to an SQL SELECT query — except that you’re not querying data in a table, but rather any object that conforms to Python’s iteration protocol, which includes a very  large number of built-in and custom-made objects.

Now, when would you want to use a list comprehension? And how does it differ from a for loop?

Using a list comprehension is appropriate whenever you want to transform data. That is, you have an iterable data source, and you want to create a new list whose elements are based on those of the data source. For  example, let’s assume that (for some reason) I want to find out how many times each character is used in /etc/passwd.  I can thus do the following, using collections.Counter:

from collections import Counter
counts = [Counter(line)
          for line in open('/etc/passwd')
          if not line.startswith("#")]

We know that “counts” is a list, because I used a list comprehension to create it. It is a list containing many Counter objects, one for each non-comment line in /etc/passwd. What if I want to find out what the most  popular character is in each line? I can modify my expression, asking the Counter object for the most common character:

counts = [Counter(line).most_common(1)
          for line in open('/etc/passwd')
          if not line.startswith("#")]

I can extend my expression even more, to get the most popular character from each line (inside of a two-element tuple in a one-element list):

counts = [Counter(line).most_common(1)[0][0]
          for line in open('/etc/passwd')
          if not line.startswith("#")]

And now I can find out how many times each most-popular character appears:

Counter([Counter(line).most_common(1)[0][0]
          for line in open('/etc/passwd')
          if not line.startswith("#")])

On my computer, the answer is:

Counter({':': 71, 'e': 4, 's': 1})

Meaning that in 71 non-comment lines, “:” is the most common, but in 4 lines it’s “e”, and in one line it’s “s”.  Now, could I have done this with a for loop?  Yes, of course — but because I’m dealing with iterables, and  because I’m using objects that work with such iterables, I can chain them together to get an answer in a way that doesn’t require me to tell Python how to do its job. I’m doing things like our accountant did, back at the  start of this article — I’m saying what I want, and letting Python do the hard work of dealing with this for me.

When would I use a for loop, then? The distinction is between whether you want to get a list back, and whether you want to execute a command a number of times.  If you want to build a list, and if it’s built on an iterable that already exists, then I’d say a list comprehension is almost certainly going the be the best bet.  But if you want to execute something a number of times without creating a list, then a comprehension is the a bad way to do it; you should use a “for” loop, instead.

It’s true that list comprehensions are faster than for loops. But most of the time, for loops are used for different things than list comprehensions. “for” loops shouldn’t be used when you want to turn one iterable structure into another; that’s for comprehensions. And you shouldn’t execute something (e.g., print) many times via a list comprehension, even if you can do so via a called function.  I’ve found that the dividing line between when to use a “for” loop, and when to use a comprehension, is clearly delineated in the minds of experienced Python developers, but very hazy among newcomers to the language, and to these ideas.

So, to summarize:

  • If you want to execute a command numerous times, use a “for” loop.
  • If you have an iterable, and want to create a new iterable, then a list comprehension is probably your best bet.
  • Building a list comprehension is sort of like working in Excel: You start with a set of data, and you create a new set of data. Any expression can be used to map from one to the other.  You don’t care about how Python does things behind the scenes; you just want to get your new data back.
  • A list comprehension consists of either two or three parts, which are often easier to understand if you put them on separate lines: (1) the expression, (2) the data source, and (3) an optional “if” statement.
  • These three lines are analogous to SQL’s SELECT, FROM, and WHERE clauses in a query.  And just as each of those (SELECT, FROM, and WHERE) can use arbitrary expressions, so too can Python’s list comprehensions use arbitrary expressions. A list comprehension will always return a list, though — just as a SELECT will always return a table-like result set.
  • Do you want to create a set, or perhaps a dictionary, rather than a list?  Then you can use a set comprehension or a dict comprehension. The idea is the same as everything I’ve said about list comprehensions, except that your result will be a single set or a single dictionary.

Do you find it difficult to work with list comprehensions?  If so, what’s hard for you about them?  And does the above help to make their use, and their syntax easier to remember?  I’m eager to hear your reactions, so that I can improve these explanations even further.

Why you should almost never use “is” in Python

It’s really tempting, when you first start to use Python, to use “is” rather than “==”.  It’s a bit more readable, and it feels like it should just work, especially when you’re dealing with integers. In a language that uses “or” and “and” instead of “||” and “&&”, it seems logical to use “is” instead of “==”. And if you try “is” with small integers, or even with short strings, you might be lulled into thinking that you should use “is” in lots of places.

But you shouldn’t.  Really, in almost no case, should you use “is”; rather, you should almost certainly use “==”.  In fact, there’s only one case in which most Python programmers should be using “is”, and that’s to check to see if something is None.

In this blog post, which is the result of many questions and discussions I’ve had with students in my Python classes, I’m going to try to describe the reasons for this — and along the way, describe some parts of how Python’s objects are allocated, and what we mean when we say that two objects are “the same.”

Let’s start with the basics: Everything in Python is an object. Every object in Python has a unique ID number, which we can retrieve from an object by using the built-in “id” function:

>>> id(5)
140236457829784

>>> id('abc')
4503718088

>>> id([1,2,3])
4504494160

Now, if two variables are pointing to the same object, they will (not surprisingly) return the same ID:

>>> x = [1,2,3]
>>> y = x
>>> id(x)
4504494160
>>> id(y)
4504494160

Given that x and y point to the same list, changes to the list will be reflected in both variables:

>>> x[0] = '!'
>>> y[1] = '?'
>>> x
['!', '?', 3]
>>> y
['!', '?', 3]

In such a case, it’s pretty clear that x and y are both pointing to precisely the same object. They aren’t just equal in value; they are one and the same — aliases for one another.

We can ask Python if this is true by using the “is” operator, also known as the “identity operator.” “is” doesn’t compare the values of x and y. Rather, it checks to see if x and y have the same ID. If so, then they are the same object. If not, then they aren’t. It’s as simple as that. Perhaps it goes without saying, but two objects that “is” each other are also “==” to each other, since an object’s value should be equal to itself:

>>> x == y
True

>>> x is y
True

>>> id(x) == id(y)
True

The above code shows that x and y have the same ID. This means that they “is” each other; we’re dealing with two names for the same object. Their values are thus equal, which is what “==” checks.

Again: The “is” operator returns “True” if two names are referring to the same object. And the “==” operator returns “True” if two names point to objects that contain the same value.

The most common usage, by far, is when we want to know if something is None. True, we would use “==”. But in both readability and speed, “is None” trumps “== None”. So your code should generally say:

if x is None:
    print("x is None!")

It shouldn’t surprise us to find out that “is” is faster than “==”. After all, “is” is implemented in C, and is a simple comparison of the IDs of the two objects. No function call is needed, and we certainly don’t need to compare the values of the two objects, which can also take some time.

The use of “is None” works because the None object is a singleton in Python. No matter what you do, id(None) will always return the same value. (Note that this value won’t stay constant across different invocations of Python.)  In other words:

>>> id(None)
4315260920

>>> id(None)
4315260920

>>> x = None
>>> id(x)
4315260920

What happens if you try to create a new instance of None? Well, we would first have to find out None’s type:

>>> type(None)
<type 'NoneType'>

Unfortunately, NoneType isn’t a defined identifier in Python:

>>> NoneType
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'NoneType' is not defined

So if we want to create a new instance of None, we’ll need to do it ourselves:

>>> type(None)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create 'NoneType' instances

Aha.  Well, that’s a shame. But I was using Python 2.7 in the above example. What if I try Python 3?

>>> type(None)
<class 'NoneType'>

>>> NoneType()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'NoneType' is not defined

>>> x = type(None)()
>>> type(x)
<class 'NoneType'>
>>> x is None
True

So no matter how you slice it, None is a singleton. Which is why you can (and should) use “is None”, rather than “== None”, in your code.

But what happens if you decide that you want to use “is” in other places? The problem is that it will sometimes work. That “sometimes” is because “is” exposes some of Python’s internal optimizations in ways that can be a bit surprising.

Strings are how I was initially introduced to the difference between “==” and “is”, and the danger of using “is” over-zealously. Two equal strings should be “==”, but are they “is”?

>>> x = 'a' * 5
>>> y = 'a' * 5
>>> x == y
True
>>> x is y
True

Well, that’s interesting — and I got the same result in Python 2.7, 3.4, and also in PyPy. But why should this be the case? One possibility is that strings are immutable, and that having Python use a single object for each string that we create, would be efficient. And indeed, this is true — so long as the string is short:

>>>> x = 'a' * 5000
>>>> y = 'a' * 5000
>>>> x == y
True
>>>> x is y
False

The above, which works the same in Python 2.7, 3.4, and in PyPy, demonstrates that Python won’t reuse just any string that we have created. There is a limit.  I experimented with things a bit, and I found that 21 is the magic length at which strings are no longer “is” to one another. That is:

>>> x = 'a' * 20
>>> y = 'a' * 20
>>> x is y
True

>>> x = 'a' * 21
>>> y = 'a' * 21
>>> x is y
False

The above was true in Python 2.7 and 3.4, and also in PyPy. However, I also found some seemingly weird behavior, which is undoubtedly because of the way in which Python byte-compiles and then executes for loops:

>>> for i in range(15,25):
        x = 'a' * i
        y = 'a' * i
        print("[{0}] x is y: {1}".format(i, x is y))

[15] x is y: False
[16] x is y: False
[17] x is y: False
[18] x is y: False
[19] x is y: False
[20] x is y: False
[21] x is y: False
[22] x is y: False
[23] x is y: False
[24] x is y: False

Wow, that’s kind of strange, no? Indeed, in a for loop, I found that the only number for which the two strings were “is” to one another was 1:

>>> for i in range(0,10):
...     x = 'a' * i
...     y = 'a' * i
...     print("[{0}] x is y: {1}".format(i, x is y))
...
[0] x is y: False
[1] x is y: True
[2] x is y: False
[3] x is y: False
[4] x is y: False
[5] x is y: False
[6] x is y: False
[7] x is y: False
[8] x is y: False
[9] x is y: False

At the same time, if you create a long literal string and assign it to a variable, you’ll likely find that the strings are “is” to one another:

>>> x = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

>>> y = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

>>> x is y
True

(Forgive the re-formatting that WordPress did to the above assignments; in Python, they were both on one line.)

I’m not sure what is going on here, but it just goes to show that you really shouldn’t use “is” unless you know what you’re doing.  And even if you think that you know what you’re doing, you might still be surprised!  Bottom line: Using “is” on strings is almost always a bad idea.

Now, this is generally something that we don’t need to think or care about very much. But let’s say that you’re working with large strings, and that these strings might repeat themselves on occasion. In such a case, you will end up with many copies of the same string. Python helps us to solve this problem by “interning” strings. Interning is a technique that has been around for many years in the programming world, which allows us to store only one copy of any given string. In Python 2, we use the built-in “intern” function. In Python 3, we must use sys.intern; intern is no longer a builtin.

“intern” takes a string (and only a string) as a parameter. It returns a reference — either to a new string that was created, or to a string that was already allocated. Thus, the length of the string doesn’t matter; even in the case of a long string, it will only be allocated a single time:

>>> from sys import intern     # Python 3 only
>>> x = intern('a' * 5000)
>>> y = intern('a' * 5000)
>>> x is y
True

As you can see, using “intern” guarantees that every unique string is allocated only once. If you use “intern” on the same string a second time, Python returns a reference to the first string.

Python uses “intern” internally for a variety of purposes.  If you’re working with long strings that repeat themselves, then it might be worth using intern. But for the most part, Python creates and allocates so many objects that a few strings here and there are probably not going to make a difference.    Certainly, you should only use “intern” once you have identified bottlenecks.

You might think that even if strings are allocated multiple times, and are thus not “is” to one another, at least integers are going to be identical. After all, Python wouldn’t allocate new objects for numbers, would it?

We can test this pretty easily, of course:

>>> x = 200
>>> y = 200
>>> x is y
True

Well, that’s encouraging, right?  Let’s try something bigger:

>>> x = 2000
>>> y = 2000
>>> x is y
False

So yes, it turns out that even integers that are equal aren’t necessarily pointing to the same object.   As Amy Hanlon pointed out in her fantastic talk about Python “wats”, this is because Python pre-allocates a number of integers. If your integer is within that range, then they will use the same object, and be “is” to one another. But if you’re outside of that range, then you’ll have two separate objects. Unless, of course, you allocate them in the same line of code:

>>> x = 2000; y = 2000
>>> x is y
True

Have I mentioned that you really shouldn’t use “is” to compare objects except for None? I hope that you’re increasingly convinced.

I’ll close this post with a bit of mischief: In theory, if two objects are “is”, then they’re pointing to the same object — which means that they should be identical to one another, and thus also give us a True response to “==”.  While Python doesn’t allow us to redefine “is”, we can redefine what an object says when we try to compare with using “==”:

>>> class Foo(object):
...     def __eq__(self, other):
...         return False
...
>>> f1 = Foo()
>>> f2 = f1
>>> f1 is f2
True
>>> f1 == f2
False

I cannot think of a situation in which this would be a desirable thing to do. But it’s fun, and allows us to sharpen our understanding of the difference between “==” and “is”.

Free Webinar on June 23rd: Introduction to Regular Expressions

If you’re a programmer, then you have likely heard about regular expressions (“regexps”) before. However, it’s also likely that you have tried to learn them, and have found them to be completely confusing. That’s not unusual; while regular expressions provide us with a powerful tool for analyzing text, their terse, dense, and cryptic syntax can make the effort not seem worthwhile.

On June 23rd, I’m going to be offering a one-hour free Webinar introducing regular expressions, showing how they can make your code more powerful and expressive.

While I’ll mostly be using Python, I’ll also show some other languages and platforms (e.g., Ruby, JavaScript, and the Unix “grep” command).

My demo and discussion will be about an hour long, and will be followed by ample time for Q&A.  My previous Webinars have been lots of fun; I hope that you’ll join in!  You can get (free) tickets at EventBrite.

And hey, if you’re an independent consultant, you can get a double dose of me on that same day; we Freelancers Show panelists will be doing our monthly Q&A just beforehand.  Come and get your questions about consulting answered by our panel of experts!

I look forward to seeing you at one or both of these events!  If you have any questions, you can e-mail me or contact me on Twitter as @reuvenmlerner.

New ebook: Jewish guide to visiting China

Jewish Guide to Visiting ChinaAs many people know, I’ve visited China seven times over the last three years, traveling there to give courses in Python and Ruby. I just got back from my most recent trip, and found it to be as fun and exciting as ever. You could say that I’ve gotten a bit obsessed with the country; I read books about China, have been taking daily Chinese lessons since August, and publish a free weekly newsletter (Mandarin Weekly) with links to useful resources for people learning Chinese.

Given that I keep kosher and Shabbat, other religious Jews are increasingly asking me for advice on what, where, and how they can be Jewishly observant when visiting China on business or pleasure. No one in China is likely to know or care about such subjects, let alone know anything about Judaism, so it can be a bit daunting to visit there for the first time.

I’ve collected my advice into a 40-page ebook, the “Jewish guide to visiting China.” If you’re a religiously observant Jew who will be visiting China for short periods of time, then I believe this guide can significantly reduce the time (and stress) you’ll need to invest before your trip.

I’m just launching it now — and for the first week it’s online, I’m offering a discount coupon (“YouTaiRen” — aka 犹太人 — the word for “Jew” in Chinese) giving 20% off of the normal $6 price. This price includes PDF, Mobi, and ePub formats, which should suit any computer or ebook reader.

Again: The Jewish guide to visiting China, now available for 20% off with the “YouTaiRen” offer code.

I expect that the book will expand significantly over time; if you purchase this book, you’re automatically entitled to updates and upgrades.

I welcome comments, suggestions, and additions!

Free one-hour Webinar about Python’s magic methods on May 6th

I’ll be giving another free one-hour Webinar about Python — this time, about the magic methods that Python offers developers who want to change the ways in which their objects work.

We’ll start off with some of the simplest magic methods, but will quickly move onto some of the more interesting and advanced ones — affecting the way that our objects are compared, formatted, hashed, pickled, and sauteed.  (OK, maybe not sauteed.)  Some familiarity with Python objects is expected, but not too much advanced knowledge is necessary.

Register now at EventBrite; if you have any questions, please contact me at reuven@lerner.co.il, or as @reuvenmlerner on Twitter.  It should be a lot of fun; I hope to see you there!

Is it hashable? Fun and games with hashing in Python

One of the basic data types that Python developers learn to use, and to appreciate, is the dictionary, or “dict.” This is the Python term for what other languages call hashes, associative arrays, hashmaps, or hash tables. Dictionaries are pervasive in Python, both in the programs that we write, and in the implementation of the language; behind every namespace or object, at least one dictionary is behind the scenes.

Dictionaries are fairly easy to use, once you get used to the rules of the road:

  1. A dictionary contains pairs, not individual elements. Each pair has two elements, a “key” and a “value.” So given a dictionary d, len(d) will return the number of pairs, not the number of individual elements.
  2. You can think of the key as a sort of index. Just as we use numeric indexes to retrieve elements of a string, list, or tuple, we use a dict’s keys to retrieve its values.
  3. The retrieval is one-way. You can get a value via its key, but you cannot get a key via its value.
  4. The retrieval takes constant time, aka O(1). You can use the “in” operator to find out if a key exists in a dictionary. If you retrieve a key that doesn’t exist, you’ll get a KeyError exception.
  5. The key must be hashable, and (if a container, such as a tuple) may only contain other hashable objects.
  6. The values may be any Python types or sizes. You can have a dict of strings, but also a dict of lists, tuples, dicts, modules, or any other objects.
  7. The keys of a dictionary are unique. If you assign d[‘a’]=1 to the dict “d”, the key “a” now exists, with a value of 1. If the key “a” already existed, then its previous value is lost.
  8. The key-value pairs in a dictionary are not ordered in any meaningful way. Do not depend on the order of the pairs in a dictionary.

To anyone familiar with dicts, or with hash tables in other languages, most of the above rules make a great deal of sense. Indeed, most of them follow naturally from the implementation of dicts: When you store d[‘a’] = 1, the dict “d” takes the key “a” and invokes the hash function on it. The result of the hash function is a number, which indicates where in the hash table the key-value pair should be stored. This is the key (no pun intended) advantage of a dictionary, and the secret of its lookup speed: The result of applying the “hash” function on our key determines where the key-value pair will be stored. Python can then jump to that location in memory, and retrieve the value associated with the key.

This also explains why you can use keys to retrieve values, but not the reverse: The location of a value in memory depends completely on its key. Moreover, while keys must be unique, values don’t have to be.

Furthermore, this explains why pairs in a dict don’t seem to be ordered in any predictable way; their ordered is determined by the hash function, which is deliberately designed to provide hard-to-predict results.

For example, I can create a simple dictionary:

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

As you can see, the printed representation of our dictionary shows the keys in the order ‘a’, ‘c’, and ‘b’, rather than order or the order in which they were inserted. Assigning to the dictionary either replaces an existing pair (if I reuse a key) or adds a new pair:

>>> d['a'] = 100
 >>> d
 {'a': 100, 'c': 3, 'b': 2}
 >>> d['z'] = [1,2,3]
 >>> d
 {'a': 100, 'c': 3, 'b': 2, 'z': [1, 2, 3]}

Almost all of this matches the rules for dict-like structures in other languages — except for rule #5, the requirement that the keys be hashable. (Or if we’re dealing with container objects, that the contained elements be hashable.) It’s reasonable to ask why this is forbidden. There aren’t a lot of times when I would like to use a list, set, or dict as a dictionary key, but it does happen. Why does Python prevent me from doing so?

The answer has to do with predictability: If I could use a list as my dictionary key, then there would be the chance of the list changing after storing it. In such a case, the list’s current hash value will be different than its previous hash value — meaning that the list will be located somewhere other than where it should be. In such a case, the key-value pair would be “lost” inside of the dict.

This can actually happen in Ruby, which doesn’t restrict the data types which can be used as keys. For example:

myarray = [1,2,3]    # create a Ruby Array 
h = {myarray => 1}   # use the array as a key

h[myarray]           # What value is associated with myarray?
   => 1              # We get 1 back, as expected! Yay!

Ruby stores our name-value pair inside of the hash, its equivalent of a dict. However, I can modify the array that is being used as a hash key:

myarray << 4        # append 4 to myarray

    => [ 1, 2, 3, 4 ]

When we stored the name-value pair in myarray, the Array had three elements. Now it has four, thus giving it a new hash value. After modifying myarray, we can ask Ruby to retrieve the value associated with it in h:

h[myarray]         # Get the value for key "myarray"
     => nil        # nil means non-existent key

In other words, the hash still has the key “myarray”, but the key-value pair is stored in the location determined by myarray.hash when we first stored it, not in its current incarnation.

Ruby’s solution to this problem is to provide a “rehash” method, which tells a hash to go through its contents, and recalculate the keys’ hash values and locations. Once you do this, the data returns:

h.rehash  # recalculate positions
    =>  { [ 1, 2, 3, 4 ] => 1 }

h[myarray]
    => 1

We thus see that in Ruby, we’re allowed to use mutable data structures as keys. The advantage is that we’re not limited, but the disadvantage is that keys might get changed, and thus provide incorrect search results.

Python, many years ago, solved this problem a different way: Instead of allowing us complete flexibility in our hash keys, Python restricted us, to (largely) immutable ones. Thus, we can use None, True, False, integers, floats, strings, and tuples — although ints and strings are the most common, in my experience. If we try to store a key-value pair in a dictionary, Python checks to make sure that the key is a hashable type:

>>> mylist = [1,2,3]

>>> d = {mylist:1}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

The “unhashable” error message that we get isn’t from the assignment to d, but rather from the call to hash() that Python makes on “mylist”. We can see this if we try to invoke the hash function directly:

>>> hash(mylist)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

It’s not really true, as we’ve seen, that lists are inherently unhashable. Rather, Python decided long ago that it would refuse to hash anything whose value might be subject to change, to avoid elements getting lost.

The hash function in Python has been described before, in more detail (and with greater knowledge) than I could provide. There are two aspects to Python’s hash function, which I’m not in a position to criticize, but which do seem strange to me:

  1. Hash functions, by their very nature, are supposed to be one-way, deterministic, but fairly unpredictable. That is, if I know the output of hash(‘a’), I shouldn’t be able to easily know what hash(‘b’) or hash(‘c’) is. But hash(‘a’) should always return the same value. In Python, this is the case for strings and tuples. But hash(), when handed an int, returns that int. Thus, hash(1) is 1, hash(100) is 100, and hash(255) is 255. This strikes me as a bit strange, and seems to violate one of the basic rules of hashing. I can only conclude that either I don’t know much about hash functions (which is quite possible), that Python doesn’t expect us to use many integers as dictionary keys, or that it just doesn’t matter that much.
  2. The hash function apparently returns -1 when it encounters an error. Thus, the hash values of both -1 and -2 are -2.

The result of a hash function doesn’t need to be unique, but it does need to evenly distribute the results, such that we’ll minimize collisions. That is, it’s possible that hash(‘a’) and hash(‘b’) will return the same value — but it should be hard to figure out which values will give us the same results. If, by some chance, all of your keys have the same hash value, then you end up with a “collision.” This is invisible to the user of the dict, except that the lookups suddenly become much slower. Imagine a dict in which 100 keys all have the same hash value; our lookup speed suddenly becomes O(n), like a list, rather than O(1), which is theoretically possible in a dict.

This apparently became an issue several years ago, when there were some attacks against Web sites running Python. Web applications often use dicts to pass incoming parameters, which means that if you choose your keys cleverly enough, you can cause a massive slowdown on a site, in a denial-of-service attack.

The solution is to add a random seed to the hashing algorithm. This isn’t implemented in Python by default, but can easily be added by invoking Python 2.7 with the -R command-line parameter, or Python 3.x with the PYTHONHASHSEED environment variable set.

Thus, in Python 2.7:

$ python -R
>>> hash('a')
-5027793331667802690
>>> hash('b')
-5027793332354350531
>>>

$ python -R
>>> hash('a')
-4154372447873558006
>>> hash('b')
-4154372448337085303
>>>

Notice how, thanks to the -R parameter, we force Python to re-seed its hash function, thus reducing the chance of a successful attack.

Python 3 took this a step further, by using an environment variable. If you set PYTHONHASHSEED  to “random”, then it behaves like Python 2, above. But if you set PYTHONHASHSEED to a numeric value, then the hash function is seeded with the number you provide. This makes it easier to test your code, but also to enjoy the extra security that the randomized hash keys provide.

Now, you would think that from everything I wrote above, that if I write my own class, it won’t be hashable. But it turns out that this is not the case:

>>> class Foo(object):
        pass

>>> f = Foo()
>>> hash(f)
273483861

According to the Python documentation, user-defined classes are hashable by default; the hash value of such an object depends on the object’s unique ID number, which we can get via the built-in “id” function. This means that the hash value of a user-defined object won’t change, regardless of any changes you might make to its attributes.

But let’s say that I want to have the hash reflect the attributes. According to the Python documentation, this means that I should define both the __hash__ method (which the built-in “hash” function will call on our object, and which must return an integer) and the __eq__ method, to check if two things are equal (since two equal objects should have equal hashes, too). I’m going to define a simple class, along with a __hash__ method:

>>> class Foo(object):
        def __init__(self, x):
            self.x = x
        def __hash__(self):
            return hash(self.x)

>>> f = Foo('a')
>>> hash(f)
12416037344
>>> hash('a')
12416037344

As the above code demonstrates, our object now returns the hash value of whatever is set on its “x” attribute. There is no difference between invoking hash(‘a’) and hash(f), assuming that f.x is ‘a’.

So now let’s put our object in a dictionary:

>>> d = {f:1}
>>> d[f]
1

So far so good, right? But now let’s be a bit evil, and change the value of f.x:

>>> f.x = 'abc'
>>> d[f]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: <__main__.Foo object at 0x109987590>

What happened? Well, we forced Python to be like the Ruby example that I provided earlier: When we created d, and used f as a key, Python stored the pair {f:1} based on the value of hash(f). But then we changed the value of f.x, which means that the value of hash(f) has changed, Which means that now, when we invoke d[f], Python will complain that there is no such key. The only way to get this key back is to use d.keys() or d.items(), which will return the key. But our ability to retrieve our value via the key, or even to check if our key exists, is now gone:

>>> f in d
False

We can have even more fun, by ensuring that the value of __hash__ changes every time we invoke it:

>>> import random
>>> class LoseMe(object):
        def __hash__(self):
            return random.randint(1,1000)

>>> x = LoseMe()
>>> hash(x)
374
>>> hash(x)
50

Now, the odds are pretty good that when I stick this object into a dictionary, I won’t be able to get it back:

>>> x = LoseMe()
>>> d = {x: 1}
>>> x in d
False
>>> x in d
False

But of course:

>>> list(d.keys())
[<__main__.LoseMe object at 0x109997a10>]

Should you ever define __hash__ to return a random value? Almost certainly not. And yet, I’d like to think that knowing how to do such a thing is both interesting and provides insights into how Python implements one of its core features.

 

Free Webinar in object-oriented Python on April 1st, 2015

Join me for an hour-long free Webinar about object-oriented Python on April 1st, 2015. I’ll discuss how to use and create classes in Python, how attributes form the core of Python’s object system, and how you should (and shouldn’t) think about Python’s objects. There will be plenty of time for Q&A. Come and join me; it’ll be a lot of fun!

You can register for the Webinar at Eventbrite:

https://eventbrite.com/event/16312775952/

I look forward to seeing you there!

How not to write an error message

Users and programmers see error messages very differently.

When a user sees an error message, they think, “Oh, no.  Something went wrong.”  Rarely, in my experience, does the user think to read the error message, or to use it as a clue toward what might have happened.  Technology is so opaque, so hard to understand, and so seemingly random that people have stopped paying attention to error messages.  The situation hasn’t been helped by the “blue screen of death,” or messages that say, “Error -16,” or the like.  The more people get such messages, the more they’re likely to tune them out.

When clients ask me for help with a problem, I often ask them to tell me what error message they saw. This question often surprises them; they don’t expect that the error message will tell them what their next step could be. But in good software, it will.

Programmers thus see error messages as helpful hints toward how a solution can be solved. (Of course, programmers and engineers are generally interested in solving problems — something that the public at large doesn’t necessarily share.)  When I see an error message, I ask, “Hmm, let’s see if this is telling me what I can do to remove the problem.” I see the error message as a helpful hint that the system’s programmer left for me, rather than an insult or yet more proof that technology is fickle.

But it turns out that when programmers are new or inexperienced with a technology, they often exhibit the same behavior as novices. This week, I taught a two-day course in Git. Now, Git is not exactly known for being an easy-to-understand system — but as I showed my students, there are many times when the error message does tell us something useful. Perhaps it’s using sui generis Git-flavored technobabble, but once you understand how Git works behind the scenes, the message will actually make sense.

My point is that even if error messages are ignored by the majority of users, they can and should be written such that people can benefit from them. Even if the number of people who will read these messages is small, you should at least give them a hint, and indicate why things when wrong.

Indeed, a good error message should say: (1) what inputs were provided that caused this error, (2) what was wrong with those inputs, and (3) what you can do to solve the problem. It’s totally fine to log lots of additional information to a logfile or database for further analysis later on. But to the degree that it’s possible, error messages should try to help people to help themselves.

That’s nice in theory, of course, In reality, there are the completely, maddeningly, stupid error messages that give all programmers a bad name.

Case in point: About two years ago, I bought a 12-trip train ticket (“kartisiya”) from Modi’in (where I live) to Bnai Brak (where I had a client). I figured that I would make enough trips to Bnai Brak that it was worth getting these tickets, and putting them on my electronic ticket card, known in Israel as a “Rav kav.” But it turned out that it was actually faster for me to get off at one of the Tel Aviv train stations, and then walk to this client’s office.

A few days ago, I decided that if I hadn’t used these tickets in two years, I should probably get them refunded. Or changed to another city. Or something.  Unfortunately, because they were on my Rav Kav, no station would refund my money. Nor could they change the tickets to be between Modi’in and another city. I was stuck with 12 tickets to a train station that I wasn’t likely to need or use in the near future.

Fortunately, Israel Railways has a fancy Web site. I went there, and found that I could submit questions to their help line via a Web form. Note that Israel Railways has removed phone numbers and e-mail addresses from their “contact us” page; if you want to contact them, you’re encouraged to (1) use a Web form, (2) send a fax, or (3) send postal mail.

So I went to the Web form, filled in my story about my 12-trip ticket to Bnai Brak, provided lots of personal details, and was greeted with the following message:

train-fail

If you don’t read Hebrew, then here’s my quick transaction of the central text:

Form for public queries

Dear customer,

The system wasn’t able to handle your query.

Thanks for contacting us!

So, which is it? Are they thanking me?  Or telling me that something went wrong?  (Or both?) And did something go wrong because of a failure on my part, or on theirs?  Should I re-submit my query?  (I did; I got the same failure a second time.)

In the end, I discovered that there is a live-chat option on the Israel Railways site. Surprisingly enough, it was staffed by a real human, who asked me what was wrong.  I told her what I wrote above — and she said that complaint has been registered, and someone will get back to me in the near future.

I believe that they will get back to me. And I believe (hope) that I’ll be able to get my money back on those tickets.

But I keep thinking about the programmer who implemented this error message, and how he or she has, without knowing it, contributed to the general public’s sense that technology is random, unfriendly, and destined to drive us mad.

Are you a programmer? If so, do you consider the content of your error messages when you write them, and what people will do with them if and when they appear? Remember that error messages are meant to be read by people, not machines — and try to write them accordingly.

A quick introduction to implementing Python iterators

When you put a piece of Python data into a “for” loop, the loop doesn’t execute on the data itself.  Rather, it executes on the data’s “iterator.”  An iterator is an object that knows how to behave inside a loop.

Let’s take that apart.  First, let’s assume that I say:

for letter in 'abc':
    print(letter)

I’m not really iterating over ‘abc’.  Rather, I’m iterating over the iterator object that I got from ‘abc’.  That is invisible and behind the scenes, but it happens all the same.  We can get the iterator of any object with the iter() function:

>>> s = 'abc'

>>> iter(s)
<iterator at 0x10a47f150>

>>> iter(s)
<iterator at 0x10a47f190>

>>> iter(s)
<iterator at 0x10a47f050>

Notice that each time we invoke iter(s), we get back a new and different object.  (We can tell, because there is a different address in memory for each one.)  That’s because each iterator is used only once.  Once you get to the end of an iterator object, the object is thrown out, and you need to get a new one.

OK, so what can we do with this iterator object?  Why do we care about it so much?  Because we can invoke the next() function on it.  Each time we do so, we’re basically telling the object that we want to get the next piece of data that it’s providing:

>>> i = iter(s)

>>> next(i)
'a'

>>> next(i)
'b'

>>> next(i)
'c'

So far, so good: Each time we invoke next(i), we ask our iterator object (i) to give us the next element.  But there are only three elements in s, which raises the question of what we’ll get when we invoke next() another time:

>>> next(i)
StopIteration

In other words, Python raises an exception (StopIteration) when we get to the end.  We can now invoke next(i) as many times as we want; we’ll always get StopIteration, which indicates that there is nothing more to get.

You can thus think of a “for” loop as a “while” loop that catches the StopIteration exception, and then leaves the loop when it happens. Consider this function:

def myfor(data):
    i = iter(data)
    while True:
        try:
            print next(i)
        except StopIteration:
            break

Now, this “myfor” function only prints the elements of the sequence it was given, so it’s not really a replacement for loop.  But it’s not a bad way to begin to understand how these things work. Our function starts off by getting an iterator for our data.  It then assumes that we are going to iterate forever on the object, using the “while True” infinite loop. However, we know that when next(i) is done providing elements of data, it will raise StopIteration.  At that point, we’ll catch the exception and return from the function.

Let’s assume that you want to make instances of your class iterable. This means that when we invoke iter() on an instance of your class, we’ll want to get back an iterator.  Which means that we’ll want to get back an object on which we can invoke next(), and either get the next object or the StopIteration exception.

The easiest way to do this is to define both __iter__ (which is invoked when you run iter() on an object) and __next__ (which is invoked when you run next() on an iterator) within your class object. That is, you’ll define __iter__ to return self, because the object is its own iterator.  And you’ll define __next__ to return the next piece of data in turn, or to raise StopIteration if there is no more data.

Remember that in an iterator, there is no “previous” or “reset” or anything of the sort.  All you can do is move forward, one item at a time, until you get to the end.

So let’s say that I want to define a simple iterator, one that returns the elements of a piece of data.  (Yes, basically what you already get built in by Python.)  We can say:

class MyIter(object):
    def __init__(self, data):
        self.data = data
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):   # In Python 2, this is just "next")
        if self.index >= len(self.data):
            raise StopIteration
        value = self.data[self.index]
        self.index += 1
        return value

Now I can say

>>> m = MyIter('abc')
>>> for letter in m:
        print(letter)

and it will work!

You can take any class you want, and make it into an iterator by adding the  __iter__ method (which returns self) and the __next__ (or in Python 2, “next”)  method.  Once you have done that, instances of MyIter can now be put inside of “for” loops, list comprehensions, or anything else that expects an “iterable” type of data.