Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I use a variable in a function before it is defined in Python?

In my code below, I am defining two functions. main and cube. I wanted main to be the start of my program, so I called cube inside of main:

>>> def main():
    number = int(input('Enter a number: '))
    cubed_number =  cube(number)
    print("The number cubed is: ", cubed_number)


>>> def cube(number):
    return number * number * number

>>> 

But I'm defining cube after main, so I thought my code would raise a NameError. However, instead of an exception being raised, Python executed my code perfectly fine:

>>> main()
Enter a number: 5
The number cubed is:  125
>>> 

What happened? Why was Python able to run my code, without knowing how cube was defined yet? Why wasn't a NameError raised?

Even stranger is that when I tried to do the samething with classes, Python did raise a NameError:

>>> class Main:
        cubed_number()


Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    class Main:
  File "<pyshell#27>", line 2, in Main
    cubed_number()
NameError: name 'cubed_number' is not defined
>>> 

What's happening here?


Note: This is not a duplicate of Why can I call a function before defining it, with only a warning?, because the answers there don't really explain why or how this behavior works in Python. I created this Q&A because the current answers for questions such as this are scattered across various questions. I also feel it would be beneficial to show what is happening behind the scenes for this to work. Feel free to edit and improve the Q&A.

like image 307
Christian Dean Avatar asked Jul 11 '17 18:07

Christian Dean


People also ask

Why do we assign a function to a variable in Python?

In Python, we can assign a function to a variable. And using that variable we can call the function as many as times we want. Thereby, increasing code reusability.

How to define a function with default values in Python?

In Python, there are other ways to define a function that can take variable number of arguments. Three different forms of this type are described below. Function arguments can have default values in Python. We can provide a default value to an argument by using the assignment operator (=). Here is an example.

What is a Python variable?

A Python variable is a symbolic name that is a reference or pointer to an object. Once an object is assigned to a variable, you can refer to the object by that name. But the data itself is still contained within the object. This assignment creates an integer object with the value 300 and assigns the variable n to point to that object.

What happens when you define a function in Python?

To understand what is happening, one must understand the distinction Python makes between defining a function, and executing a function. When Python encounters a function definition, it compiles the function into a code object.


1 Answers

To understand what is happening, one must understand the distinction Python makes between defining a function, and executing a function.

Definition vs Execution

When Python encounters a function definition, it compiles the function into a code object.

A code object is an internal structure Python uses to hold the bytecode associated with a specific executable block of code. It also holds other information Python needs to execute the bytecode such as constants and local variable names. The documentation gives a much more more extensive overview of what code objects are.

The code object is then used to construct a function object. The function object's code object is then used to execute the function when it is later called. Python would not execute the function, it would only compile the function into an object that could be used later for execution. The only time Python executes a function, is when the function is called.

Here is the relevant part from the documentation which mentions this:

A function definition is an executable statement. Its execution binds the function name in the current local namespace to a function object (a wrapper around the executable code for the function). This function object contains a reference to the current global namespace as the global namespace to be used when the function is called.

The function definition does not execute the function body; this gets executed only when the function is called.

Because of this distinction, Python cannot verify a name is actually defined until the function is called. Thus, you are allowed to use currently non-existent names in the function body. As long as the name is defined when the function is called, Python will not raise an error.

Here is an example. We define a function func that adds two variables together; a and b:

>>> def func():
...     return a + b

As you can see Python raised no error. This is because it simply compiled func. It did not attempt to execute the function, so it does not see that a and b are not defined.

We can disassemble func's code object, and see what the bytecode looks like using the dis module. This will tell us more about what Python is doing:

>>> from dis import dis
>>> dis(func)
  2           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

Python encoded two LOAD_GLOBAL instructions in the byte-code. The arguments to the instructions are the variable names a and b respectively.

This shows that Python did see that we were attempting to reference two variables when compiling our function and created bytecode instructions to do so. But it does not attempt to actually execute the instructions, until the function is called.

Let's see what happens when we attempt to execute the bytecode for func by calling it:

>>> func()
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    func()
  File "<pyshell#14>", line 2, in func
    return a + b
NameError: name 'a' is not defined

As you can see, Python raised a NameError. This is because it attempted to execute the two LOAD_GLOBAL instructions, but discovered that the names where undefined in the global scope.

Now let's see what happens if we defined two variables a and b before calling func:

>>> a = 1
>>> b = 2
>>> 
>>> func()
3

The reason the above worked, is because when Python was executing func's byte-code, it was able to find the global variables a and b, and used those to execute the function.

The same applies to the example is the question. When main was compiled, Python "saw" we were trying to call a variable named cube and generated an instruction to get the value of cube. But it did not attempt to find a callable object named cube until the instructions were executed. And by the time main's byte-code was executed(eg. main was called), a function named cube was defined, so Python did not raise an error.

If we try to call main before cube is defined however, we'll get a name error for the same reasons in the above example:

>>> def main():
...     number = int(input('Enter a number: '))
...     cubed_number =  cube(number)
...     print("The number cubed is: ", cubed_number)
... 
>>> main()
Enter a number: 23
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    main()
  File "<pyshell#22>", line 3, in main
    cubed_number =  cube(number)
NameError: name 'cube' is not defined

What about the class?

Python handles class definitions a bit differently from function definitions.

When Python encounters a class definition, it creates a code object for the class as with the function. However, Python also allows classes to have namespaces that are executed during the class definition. Python does not wait to execute the classes namespace because any variables defined should belong to the class. Thus, any names used inside of a classes name-space must be defined for usage during class-definition time.

The documentation for class definitions touches on this:

The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved.

However, this does not apply to methods. Python treats undefined names in methods as with functions, and allows you to use them when defining the method:

>>> class Class:
...     def method(self):
...         return var
...
>>> var = 10
>>> cls = Class()
>>> cls.method()
10
like image 184
Christian Dean Avatar answered Oct 24 '22 13:10

Christian Dean