View Discussion
Improve Article
Save Article
View Discussion
Improve Article
Save Article
Python variable assignment is different from some of the popular languages like c, c++ and java. There is no declaration of a variable, just an assignment statement.
Let us see why?
When we declare a variable in C or alike languages, this sets aside an area of memory for holding values allowed by the data type of the variable. The memory allocated will be interpreted as the data type suggests. If it’s an integer variable the memory allocated will be read as an integer and so on. When we assign or initialize it with some value, that value will get stored at that memory location. At compile time,
initial value or assigned value will be checked. So we cannot mix types. Example: initializing a string value to an int variable is not allowed and the program will not compile.
But Python is a dynamically typed language. It doesn’t know about the type of the variable until the code is run. So declaration is of no use. What it does is, It stores that value at some memory location and then binds that variable name to that memory container. And makes the contents of the container accessible through that variable name. So the data type does not matter. As it will get to know the type of the value at run-time.
x
=
6
print
[
type
[x]]
x
=
'hello'
print
[
type
[x]]
Output:
In this article we describe how giving away dynamic typing of variables can increase the performance of the program.
© CC-BY-NC-SA 4.0 by CSC - IT Center for Science Ltd.
Python is both a strongly typed and a dynamically typed language.
Strong typing means that variables do have a type and that the type matters when performing operations on a variable. Dynamic typing means that the type of the variable is determined only during runtime.
Due to strong typing, types need to be compatible with respect to the operand when performing operations. For example Python allows one to add an integer and a floating point number, but adding an integer to a string produces error.
Due to dynamic typing, in Python the same variable can have a different type at different times during the execution. Dynamic typing allows for flexibility in programming, but with a price in performance.
Everything is an object
One
of the key features of Python is that everything is an object, and the type is just one attribute of an object. As an illustration, we can assign a single integer to a variable, and use the Python built-in function dir
for
finding out the attributes of the object. If you execute the following two
lines in an interactive interpreter, it should become clear that a Python
integer is much more complex than just a number. Please feel free to
experiment also with other types!
The
fact that everything is an object means that there is a lot of “unboxing”
and “boxing” involved when Python performs operations with variables. For
example, when just adding two integers
there are several steps Python needs to do:
- Check the types of both operands
- Check whether they both support the + operation
- Extract the function that performs the + operation [due to operator
overloading objects can have a custom definition for addition] - Extract the actual values of the objects
- Perform the + operation
- Construct a new integer object for the result
Due to the fact that Python is dynamically typed, the interpreter cannot know
beforehand what type of objects one is dealing with, and everytime
two
variables are added one needs to perform all the above steps.
Adding static type information
What if one knows that e.g. in a certain function the variables have always
the same type? That’s where Cython steps in: Cython allows one to add static
typing information so that boxing and unboxing are not needed, and one can
operate directly with the actual values.
When Cythonizing a Python code, static type information can be added either:
- In function signatures by prefixing the formal arguments by their type
- By declaring variables with the cdef Cython keyword, followed by the
the type
For example, a simple Python function adding two objects could be Cythonized
as follows:
def add [int x, int y]:
cdef int result
result = x + y
return result
The function works now only with integers but with less boxing/unboxing
overheads.
The types provided in Cython code are C types, and the variables with
type
information are pure C variables and not Python objects. When calling a
Cythonized function from Python, there is an automatic conversion from the
Python object of actual arguments to the C value of formal argument, and when
returning a C variable it is converted to corresponding Python object.
Automatic conversions are carried out also in most cases within the Cython
code where both Python objects and C variables are involved.
The table below lists the most common
C types and their corresponding Python
types. More information can be found in the
Cython documentation.
int | int, long |
int, float | float, double |
str/bytes | char * |
int, long | int |
float, double | float |
char * | str/bytes |
Static typing in Mandelbrot kernel
In week 1 we did a performance analysis of Mandelbrot fractal in Step 1.11. The analysis revealed
that the kernel function in module mandelbrot.py was the most time critical one. Thus, let’s make mandelbrot.py into Cython module
mandelbrot.pyx and introducing static typing to the
function kernel.
Pure Python version was:
def kernel[zr, zi, cr, ci, lim, cutoff]:
''' Computes the number of iterations `n` such that
|z_n| > `lim`, where `z_n = z_{n-1}**2 + c`.
'''
count = 0
while [[zr*zr + zi*zi]