print can be deceiving

This is more of a short musing than a blog post, but maybe some insight that can help a beginner. This is written specifically with respect to Python, but I think some of the basic ideas translate to other object oriented languages.

when print was your best friend

When someone is new to Python, and more so when new to programming in general, print is your best friend. Learning a new language is done by comparing what you expect happen and what actually happen. The prototypical example in any language is

print 'Hello World!'

and a new comer to Python going through something like Learn Python the Hard Way (LPTHW) will spend a number of lessons where a script ends with a print statement revealing the answer. This is notable because in a production system, print isn't used much at all, unless something goes wrong.

when print isn't used

There are varying types of "systems" you can build in Python: (in rough order of rising complexity) scripts, packages, applications, web services/apis. This list is certainly not exhaustive, but except for the most basic scripts that calculate something like a summary statistic, print is rarely used.

Most python "systems" (or really in any language) are used to deal with data structures that are rarely reduced down to one dimension. For example, Project Euler, Problem 1 challenges you to find the sum of all the multiples of 3 or 5 below 1000. Python accomplishes this in 1 step:

print sum([x for x in range(1000) if x % 3 == 0 or x % 5 == 0])

In this case, we are looking for the answer to the problem. However, most "systems" aren't designed to answer a problem, but to solve it. A python module typically defines certain models and methods for manipulating those methods. A web application models objects like users and is able to pass objects specific to that user to the browser. A web scrapper loads external webpages, allowing you to collect relevant data from the page. You are unlikely to use print to handle that data. More likely you will store it to a database or a flat file.

when print can be deceiving

You will never stop using print, it just will stop to be the intention of your programs. However, when ever working with a new module or framework, often times you are back to Hello World. By now you are able to read the docs and write a small program without any issue. But as you start to explore the more subtle features, often the docs will simply imply the correct implementation.

print will again be your best friend when you mess something up and you're trying to figure out where you went wrong. However, print can often hide important things when you are debugging. For example, in an interactive python session:

>>> print 2
2
>>> print '2'
2

print isn't going let you know if the object you are printing here is a int or a str. Even more insidious, it could be a custom class with an attribute val = 2 with it's __repr__ method defined as print self.val. This is roughly the problem I recently ran into.

I was working with the python-twitter module and trying to deal with different twitter errors differently. The pseudo-code to figure out the errors:

try:
  x = twitter_api.call()
except TwitterError as error:
  print error

The result I got here in one case was [{'message':'error code 1'}] and in the other error code 2. Now this is kind of a pain in the ass, and really the result of python-twitter handling different errors with different methods (probably an issue worth fixing), but it's solvable with

try:
  x = twitter_api.call()
except:
  if type(error) == list and error[0]['message'] == 'error code 1':
    #do whatever you need for error message 1
  elif error == 'error code 2':
    #do whatever you need for error message 2
  else:
    raise #Errors should never pass silently.

The problem here is that error is not a str but a instance of the TwitterError class, which has an attribute message that is printed when print is called on the object. This is expect behavior, and quite obviously implied by TwitterError as error. But easy enough to miss. A find and replace of error to error.message on the above code fixes it.

One really handy way to help with these types of situations is running python -i your_script.py. This will open up an interactive session after the script exits upon an error or on completion. In this case, I was able observe the error variable after the TwitterError was raised when I thought that it would be captured. So remember, print doesn't always give you the full picture.