A short Python class puzzle
Here’s a short Python puzzle that I use in many of my on-site courses, which I have found to be useful and instructive: Given the following short snippet of Python, which letters will be printed, and in which order?
print("A") class Person(object): print("B") def __init__(self, name): print("C") self.name = name print("D") print("E") p1 = Person('name1') p2 = Person('name2')
Think about this for a little bit before looking at the answer, or executing it on your computer.
Let’s start with the answer, then:
A B D E C C
Let’s go through these one by one, to understand what’s happening.
The initial “A” is printed because… well, because it’s the first line of the program. And in a Python program, the lines are executed in order — starting with the first, then the second, and so forth, until we reach the end. So it shouldn’t come as a surprise that the “A” line is printed first.
But what is surprising to many people — indeed, the majority of people who take my courses — is that we next see “B” printed out. They are almost always convinced that the “B” won’t be printed when the class is defined, but rather when… well, they’re really sure when “B” will be printed.
Part of the problem is that Python’s keywords “class” and “def” look very similar. The former defines a new class, and the latter defines a new function. And both start with a keyword, and take a block of code. They should work the same, right?
Except that “class” and “def” actually work quite differently: The “def” keyword creates a new function, that’s true. However, the body of the function doesn’t get executed right away. Instead, the function body is byte compiled, and we end up with a function object.
This means that so long as the function body doesn’t contain any syntax errors (including indentation errors), the function object will be created. Indeed, the following function will produce an error when run, but won’t cause a problem when being defined:
def foo(): asdfafsafasfsafavasdvadsvas
This is not at all the case for the “class” keyword, which creates a new class. And if we think about it a bit, it stands to reason that “class” cannot work this way. Consider this: Immediately after I’ve defined a class, the methods that I’ve defined are already available. This means that the “def” keyword inside of the class must have executed. And if “def” executed inside of a class, then everything else executes inside of a class, also.
Now, under most normal circumstances, you’re not going to be invoking “print” from within your class definition. Rather, you’ll be using “def” to define methods, and assignment to create class attributes. But both “def” and assignment need to execute if they are to create those methods and attributes! Their execution cannot wait until after the class is already defined.
I should also add that in Python, a class definition operates like a module: What would looks like an assignment to a global variable inside of the class definition is actually the creation of an attribute on the class (module) itself. And of course, the fact that the body of a class definition executes line by line makes it possible to use decorators such as @staticmethod and @property.
In short, “def” inside of a class definition creates a new function object, and then creates a class-level attribute with the same name as your function.
So, when you define a class, Python executes each line, in sequence, at the time of definition. That’s why “B” is printed next.
Why is “C” not printed next? Because the body of a function isn’t executed when the function is defined. So when we define our __init__ method, “print” doesn’t run right away. It does, however, run one time for each of the two instances of “Person” we create at the end of the program, which is why “C” is printed twice.
However, before “C” is printed, the class definition still needs to finish running. So we first see “D” (inside of the class definition), and then “E” (just after the class is defined).
Over the years, I’ve found that understanding what goes on inside of a class definition helps to understand many of the fundamental principles of Python, and how the language is implemented. At the same time, I’ve found that even in my advanced Python classes, a large number of developers don’t answer this puzzle correctly, which means that even if you work with Python for a long time, you might not have grasped the difference between “class” and “def”.