Python's execution model
In this paragraph, I would like to introduce you to a few very important concepts, such as scope, names, and namespaces. You can read all about Python's execution model in the official Language reference, of course, but I would argue that it is quite technical and abstract, so let me give you a less formal explanation first.
Names and namespaces
Say you are looking for a book, so you go to the library and ask someone for the book you want to fetch. They tell you something like "second floor, section X, row three". So you go up the stairs, look for section X, and so on.
It would be very different to enter a library where all the books are piled together in random order in one big room. No floors, no sections, no rows, no order. Fetching a book would be extremely hard.
When we write code we have the same issue: we have to try and organize it so that it will be easy for someone who has no prior knowledge about it to find what they're looking for. When software is structured correctly, it also promotes code reuse. On the other hand, disorganized software is more likely to expose scattered pieces of duplicated logic.
First of all, let's start with the book. We refer to a book by its title and in Python lingo, that would be a name. Python names are the closest abstraction to what other languages call variables. Names basically refer to objects and are introduced by name binding operations. Let's make a quick example (notice that anything that follows a #
is a comment):
>>> n = 3 # integer number >>> address = "221b Baker Street, NW1 6XE, London" # S. Holmes >>> employee = { ... 'age': 45, ... 'role': 'CTO', ... 'SSN': 'AB1234567', ... } >>> # let's print them >>> n 3 >>> address '221b Baker Street, NW1 6XE, London' >>> employee {'role': 'CTO', 'SSN': 'AB1234567', 'age': 45} >>> # what if I try to print a name I didn't define? >>> other_name Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'other_name' is not defined
We defined three objects in the preceding code (do you remember what are the three features every Python object has?):
- An integer number
n
(type:int
, value:3
) - A string
address
(type:str
, value: Sherlock Holmes' address) - A dictionary
employee
(type:dict
, value: a dictionary which holds three key/value pairs)
Don't worry, I know you're not supposed to know what a dictionary is. We'll see in the next chapter that it's the king of Python data structures.
Tip
Have you noticed that the prompt changed from >>>
to ...
when I typed in the definition of employee? That's because the definition spans over multiple lines.
So, what are n
, address
and employee
? They are names. Names that we can use to retrieve data within our code. They need to be kept somewhere so that whenever we need to retrieve those objects, we can use their names to fetch them. We need some space to hold them, hence: namespaces!
A namespace is therefore a mapping from names to objects. Examples are the set of built-in names (containing functions that are always accessible for free in any Python program), the global names in a module, and the local names in a function. Even the set of attributes of an object can be considered a namespace.
The beauty of namespaces is that they allow you to define and organize your names with clarity, without overlapping or interference. For example, the namespace associated with that book we were looking for in the library can be used to import the book itself, like this:
from library.second_floor.section_x.row_three import book
We start from the library
namespace, and by means of the dot (.
) operator, we walk into that namespace. Within this namespace, we look for second_floor
, and again we walk into it with the .
operator. We then walk into section_x
, and finally within the last namespace, row_tree
, we find the name we were looking for: book
.
Walking through a namespace will be clearer when we'll be dealing with real code examples. For now, just keep in mind that namespaces are places where names are associated to objects.
There is another concept, which is closely related to that of a namespace, which I'd like to briefly talk about: the scope.
Scopes
According to Python's documentation, a scope is a textual region of a Python program, where a namespace is directly accessible. Directly accessible means that when you're looking for an unqualified reference to a name, Python tries to find it in the namespace.
Scopes are determined statically, but actually during runtime they are used dynamically. This means that by inspecting the source code you can tell what the scope of an object is, but this doesn't prevent the software to alter that during runtime. There are four different scopes that Python makes accessible (not necessarily all of them present at the same time, of course):
- The local scope, which is the innermost one and contains the local names.
- The enclosing scope, that is, the scope of any enclosing function. It contains non-local names and also non-global names.
- The global scope contains the global names.
- The built-in scope contains the built-in names. Python comes with a set of functions that you can use in a off-the-shelf fashion, such as
print
,all
,abs
, and so on. They live in the built-in scope.
The rule is the following: when we refer to a name, Python starts looking for it in the current namespace. If the name is not found, Python continues the search to the enclosing scope and this continue until the built-in scope is searched. If a name hasn't been found after searching the built-in scope, then Python raises a NameError
exception, which basically means that the name hasn't been defined (you saw this in the preceding example).
The order in which the namespaces are scanned when looking for a name is therefore: local, enclosing, global, built-in (LEGB).
This is all very theoretical, so let's see an example. In order to show you Local and Enclosing namespaces, I will have to define a few functions. Don't worry if you are not familiar with their syntax for the moment, we'll study functions in Chapter 4, Functions, the Building Blocks of Code. Just remember that in the following code, when you see def
, it means I'm defining a function.
scopes1.py
# Local versus Global # we define a function, called local def local(): m = 7 print(m) m = 5 print(m) # we call, or `execute` the function local local()
In the preceding example, we define the same name m
, both in the global scope and in the local one (the one defined by the function local). When we execute this program with the following command (have you activated your virtualenv?):
$ python scopes1.py
We see two numbers printed on the console: 5
and 7
.
What happens is that the Python interpreter parses the file, top to bottom. First, it finds a couple of comment lines, which are skipped, then it parses the definition of the function local
. When called, this function does two things: it sets up a name to an object representing number 7 and prints it. The Python interpreter keeps going and it finds another name binding. This time the binding happens in the global scope and the value is 5. The next line is a call to the print
function, which is executed (and so we get the first value printed on the console: 5
).
After this, there is a call to the function local
. At this point, Python executes the function, so at this time, the binding m = 7
happens and it's printed.
One very important thing to notice is that the part of the code that belongs to the definition of the function local is indented by four spaces on the right. Python in fact defines scopes by indenting the code. You walk into a scope by indenting and walk out of it by unindenting. Some coders use two spaces, others three, but the suggested number of spaces to use is four. It's a good measure to maximize readability. We'll talk more about all the conventions you should embrace when writing Python code later.
What would happen if we removed that m = 7
line? Remember the LEGB rule. Python would start looking for m
in the local scope (function local
), and, not finding it, it would go to the next enclosing scope. The next one in this case is the global one because there is no enclosing function wrapped around local
. Therefore, we would see two number 5
printed on the console. Let's actually see how the code would look like:
scopes2.py
# Local versus Global
def local():
# m doesn't belong to the scope defined by the local function
# so Python will keep looking into the next enclosing scope.
# m is finally found in the global scope
print(m, 'printing from the local scope')
m = 5
print(m, 'printing from the global scope')
local()
Running scopes2.py
will print this:
(.lpvenv)fab@xps:ch1$ python scopes2.py 5 printing from the global scope 5 printing from the local scope
As expected, Python prints m
the first time, then when the function local
is called, m
isn't found in its scope, so Python looks for it following the LEGB chain until m
is found in the global scope.
Let's see an example with an extra layer, the enclosing scope:
scopes3.py
# Local, Enclosing and Global def enclosing_func(): m = 13 def local(): # m doesn't belong to the scope defined by the local # function so Python will keep looking into the next # enclosing scope. This time m is found in the enclosing # scope print(m, 'printing from the local scope') # calling the function local local() m = 5 print(m, 'printing from the global scope') enclosing_func()
Running scopes3.py
will print on the console:
(.lpvenv)fab@xps:ch1$ python scopes3.py 5 printing from the global scope 13 printing from the local scope
As you can see, the print
instruction from the function local
is referring to m
as before. m
is still not defined within the function itself, so Python starts walking scopes following the LEGB order. This time m
is found in the enclosing scope.
Don't worry if this is still not perfectly clear for now. It will come to you as we go through the examples in the book. The Classes section of the Python tutorial (official documentation) has an interesting paragraph about scopes and namespaces. Make sure you read it at some point if you wish for a deeper understanding of the subject.
Before we finish off this chapter, I would like to talk a bit more about objects. After all, basically everything in Python is an object, so I think they deserve a bit more attention.
Tip
Downloading the example code
You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
Object and classes
When I introduced objects in the A proper introduction section, I said that we use them to represent real-life objects. For example, we sell goods of any kind on the Web nowadays and we need to be able to handle, store, and represent them properly. But objects are actually so much more than that. Most of what you will ever do, in Python, has to do with manipulating objects.
So, without going too much into detail (we'll do that in Chapter 6, Advanced Concepts – OOP, Decorators, and Iterators), I want to give you the in a nutshell kind of explanation about classes and objects.
We've already seen that objects are Python's abstraction for data. In fact, everything in Python is an object. Numbers, strings (data structures that hold text), containers, collections, even functions. You can think of them as if they were boxes with at least three features: an ID (unique), a type, and a value.
But how do they come to life? How do we create them? How to we write our own custom objects? The answer lies in one simple word: classes.
Objects are, in fact, instances of classes. The beauty of Python is that classes are objects themselves, but let's not go down this road. It leads to one of the most advanced concepts of this language: metaclasses. We'll talk very briefly about them in Chapter 6, Advanced Concepts – OOP, Decorators, and Iterators. For now, the best way for you to get the difference between classes and objects, is by means of an example.
Say a friend tells you "I bought a new bike!" You immediately understand what she's talking about. Have you seen the bike? No. Do you know what color it is? Nope. The brand? Nope. Do you know anything about it? Nope. But at the same time, you know everything you need in order to understand what your friend meant when she told you she bought a new bike. You know that a bike has two wheels attached to a frame, a saddle, pedals, handlebars, brakes, and so on. In other words, even if you haven't seen the bike itself, you know the concept of bike. An abstract set of features and characteristics that together form something called bike.
In computer programming, that is called a class. It's that simple. Classes are used to create objects. In fact, objects are said to be instances of classes.
In other words, we all know what a bike is, we know the class. But then I have my own bike, which is an instance of the class bike. And my bike is an object with its own characteristics and methods. You have your own bike. Same class, but different instance. Every bike ever created in the world is an instance of the bike class.
Let's see an example. We will write a class that defines a bike and then we'll create two bikes, one red and one blue. I'll keep the code very simple, but don't fret if you don't understand everything about it; all you need to care about at this moment is to understand the difference between class and object (or instance of a class):
bike.py
# let's define the class Bike class Bike: def __init__(self, colour, frame_material): self.colour = colour self.frame_material = frame_material def brake(self): print("Braking!") # let's create a couple of instances red_bike = Bike('Red', 'Carbon fiber') blue_bike = Bike('Blue', 'Steel') # let's inspect the objects we have, instances of the Bike class. print(red_bike.colour) # prints: Red print(red_bike.frame_material) # prints: Carbon fiber print(blue_bike.colour) # prints: Blue print(blue_bike.frame_material) # prints: Steel # let's brake! red_bike.brake() # prints: Braking!
Tip
I hope by now I don't need to tell you to run the file every time, right? The filename is indicated in the first line of the code block. Just run $ python filename
, and you'll be fine.
So many interesting things to notice here. First things first; the definition of a class happens with the class
statement (highlighted in the code). Whatever code comes after the class
statement, and is indented, is called the body of the class. In our case, the last line that belongs to the class definition is the print("Braking!")
one.
After having defined the class we're ready to create instances. You can see that the class body hosts the definition of two methods. A method is basically (and simplistically) a function that belongs to a class.
The first method, __init__
is an initializer. It uses some Python magic to set up the objects with the values we pass when we create it.
Note
Every method that has leading and trailing double underscore, in Python, is called magic method. Magic methods are used by Python for a multitude of different purposes, hence it's never a good idea to name a custom method using two leading and trailing underscores. This naming convention is best left to Python.
The other method we defined, brake
, is just an example of an additional method that we could call if we wanted to brake the bike. It contains just a print
statement, of course, it's an example.
We created two bikes then. One has red color and a carbon fiber frame, and the other one has blue color and steel frame. We pass those values upon creation. After creation, we print out the color property and frame type of the red bike, and the frame type of the blue one just as an example. We also call the brake
method of the red_bike
.
One last thing to notice. You remember I told you that the set of attributes of an object is considered to be a namespace? I hope it's clearer now, what I meant. You see that by getting to the frame_type
property through different namespaces (red_bike
, blue_bike
) we obtain different values. No overlapping, no confusion.
The dot (.
) operator is of course the means we use to walk into a namespace, in the case of objects as well.