There’s something I want you to learn. I can’t tell you what it is right now. You’ll have to trust me blindly and follow the instructions closely.
Part 1 — Be a challenger
We’re going to look at some buggy python code. Don’t leave just yet if you have never written python before! You will find something to apply to you preferred everyday language, I promise. If I lied, you are allowed to insult me abundantly through your favorite channel (here’s my Twitter, you’re welcome). Here’s your code, commented so that you know what happens.
def foo(a, methods=): # methods defaults to  if not specified for i in range(3): # Append an anonymous function, which returns `x + i` # when given `x` methods.append(lambda x: x + i) sum_methods = 0 # For each function `method` of in `methods`, sum `method(a)` for method in methods: sum_methods += method(a) return sum_methods
You expect your function to output the following:
- foo(0) = (0 + 0) + (0 + 1) + (0 + 2) = 3
- foo(1) = (1 + 0) + (1 + 1) + (1 + 2) = 6
- foo(2) = (2 + 0) + (2 + 1) + (2 + 2) = 9
- foo(2, [lambda x: x]) = ((2)) + ((2 + 0) + (2 + 1) + (2 + 2)) = 11
Simple, right? Let’s try it in our terminal.
>>> foo(0) 6 >>> foo(1) 18 >>> foo(2) 36 >>> foo(2, [lambda x: x]) 14
Well, there seems to be a minor error. But wait, could we have missed something? Let’s check again.
>>> foo(0) 24 >>> foo(1) 45 >>> foo(2) 72
But, you’re not going to get beaten down by this, right? You will pull your shirtsleeves up your arms (provided you wear a shirt, which would be unusual but we’re not judging), and you will find out what is wrong using well-placed print statements or other debugging practices you know.
Set a stopwatch, try to debug the code by clicking here, and please let me know how much time it took you to understand and fix what’s wrong in the comments, and what practices you used. Keep both
for loops and don’t change the
foo function calls because it would be too easy. Got it? Go!
Part 2 — Behold the Light
Before jumping to the answer, let’s talk about debuggers. Every language I know has a way to be debugged. Debuggers lets you explore each line of the code in a convenient way.
Every debugger has at least the following commands, that you can use directly in python code:
- Set a break point, where the execution will halt:
pdb.set_trace(). I usually set my breakpoints with
import pdb; pdb.set_trace()since Python is not bothered with importing the same library several times and in an inline fashion.
- Continue until next break point:
- Next line:
- List the surrounding code:
Debuggers are Life
So how much quicker could you have been by using a debugger instead of trial-and-error and print statements?
I’ve tried this hypothesis on two separate groups of people: one where they used
Now imagine you encountered these bugs hidden in a much bigger program? I know a lot of developers that spend hours debugging a bug with
l command. Debuggers walk you through the code and they make understanding some new code much easier.
Do you want your solution then? I’ve made a video of me using a python debugger (don’t mind the occasional typos please). It’s a bit long but I kept it real-time so you can see the debugging process more clearly. Here’s the video:
methods was broken because lists in python are mutable objects. It means that when
methods is set to the default value (the empty list) and is then changed, the default value is also updated (and becomes the empty list with one more element). This is a common pitfall that you can fix using a
None default value. Here is the full corrected code:
def methods_generator(i): # Scopes the i to not overwrite it return lambda x: x + i def foo(a, methods=None): if methods is None: #Prevents the default value to be changed methods =  for i in range(3): methods.append(methods_generator(i)) sum_methods = 0 for method in methods: sum_methods += method(a) return sum_methods
Part 3 — Debugging resources
I promised I would write about other languages, and I will keep this promise right now! Here are useful resources I came across, please don’t hesitate to submit more! I also including resources about code linting (that analyzes your code looking for typos and common errors) and gotchas (which are common pitfalls in a language, just like the mutable list default value above).
- Configuring your debugger in PHPStorm with XDebug through a Vagrant machine
- An Atom package for PHP debugging
- A linter for PHP in atom
- PHP gotchas (use Google for more)
- To debug your front-end code, you can set a breakpoint by typing
debuggeranywhere in your code. The execution will halt only if you have the developer tools opened in your browser! Make sure you don’t commit it :).
- Debugging in NodeJS
- Additional debugging tips
- You can also use ipdb instead of the native pdb library to debug with ipython (trust me it’s great)
- If you use Jupyter notebooks you can debug easily in them
- Though I prefer flake8, here is a list of linting tools for python.
- Python gotchas (thanks @christianwitts!)
- Debugging in Visual Studio
- There’s an embedded linter in Visual Studio called FxCop
- C# and .NET gotchas
Happy debugging, happy life!