Do We Have Pointers in Python?
- In the context of high-level languages such as C++ or Java, pointers are simply variables that store the memory address of other variables or objects. It manages the code very well. It may be difficult for beginners, but it is an important concept in the program. However, it can result in a variety of memory management bugs.
- Python does not have explicit pointers like other languages, although they are implemented beneath the hood. Types like lists, dictionary, class, and objects, etc act like pointers in python under the hood.
Why Python doesn’t have Pointers?
It is unclear why pointers are not supported in python. Could a Python pointer exist natively? Python's fundamental principle is its simplicity, yet the pointer violated the Zen of Python. Pointers are primarily encouraged to make implicit modifications rather than explicit changes. They are also difficult to understand, especially for beginners.
Pointers tend to add complexity to code, which is undesirable in Python, which prioritizes usability over performance. As a result, Python does not support pointers. However, python has certain advantages to utilizing a pointer.
Before we can understand the pointer in Python, we must first understand the following concepts.
a) Objects in Python
Everything in Python is an object, including classes, functions, variables, etc. Each object has at least three data pieces.
- Reference count: The reference count in python is used for memory management. Python memory management consists of a private heap containing all Python objects and data structures. The Python memory manager is in charge of managing this private heap.
- Type: The CPython layer is used as the type to provide type safety at runtime.
- Value: Value represents the actual value of the object.
However, if we go deeper into this object, we will discover that not all objects are the same. The key contrast between object types is immutable and mutable. First and foremost, we must comprehend the difference between object types because it explores the pointer in Python.
b) Immutable vs Mutable Objects
There are two kinds of objects in Python:
- Immutable objects cannot be modified, whereas
- Mutable objects can be modified
Let's look at the table below of common types and whether they are mutable or not.
Object | Type |
---|---|
Int | Immutable |
Float | Immutable |
Bool | Immutable |
List | Mutable |
Set | Mutable |
Complex | Mutable |
Tuple | Immutable |
Frozenset | Immutable |
Dict | Mutable |
Understanding Python Variables
a) Variables in C vs Variables in Python
Variables in Python are not the same as those in C or C++.
More specifically, Python has no variables. Instead, they are addressed by names. Let's look at an example to see how variables in Python differ from those in fundamental programming languages like C and C++.
Let's start by defining a variable in C.
This single line of code:
- Allocates memory for the specified data type, which is int in this case.
- Allocates a value of 11 to the memory location, say 0x5f3.
- This indicates that the variable n has a value of 11.
If you want to change the value of n later, you can do it in C.
The value 30 is now assigned to memory location 0x5f3. Because variable n is a mutable object, its location did not change even though its value did. That is, the variable v defined here is not only its name but also its memory location.
Now, let's assign the value of n to a new variable.
A new variable with the value 30 is now created, but it will have its own memory location and will not use the memory location of the variable whose value is copied.
Now, let's do the same thing in python. Let's define a variable in python.
This single line of code:
- This function creates a new Python object.
- Sets the Python object's data type to integer.
- The PyObject is given the value 11.
- Creates a name n and refers n to the newly created PyObject.
- 1 is added to the refcount of the newly created Python object.
Unlike in C, a new object is created, and that object owns the variable's memory location.
The name n no longer owns any memory locations.
Now let's change the value of n and assign it with a new value.
This single line of code:
-
This first creates a new PyObject.
-
Sets the PyObject's data type to integer.
-
Assigns a value of 22 to the PyObject.
-
Points n to the newly created PyObject.
-
Increases the newly created PyObject's refcount by 1.
-
Reduces the refcount of an existing PyObject by 1.
The variable n now references the newly created object. The first object, which had a refcount of 11, now has a refcount of zero. The garbage collector will clean this up.
Now let's assign the value of n to the new variable.
In Python, this creates a new name rather than a new object.
You can test this by running the code below.
The result will be true. It should be kept in mind that new is still an immutable object. Any operation on new will result in the creation of a new object.
How to Simulate Pointers in Python?
Although Python does not support pointers, we can reap the benefits of doing so. Python provides alternative methods for using the pointer. These two options are listed below.
a) Using Mutable Types as Pointers
In the previous section, we defined mutable type objects; we can treat them as if they were pointers to simulate pointer behavior. Let's take a look at an example.
In the above code, x is assigned a value of 23. The current value is printed, and then the value of p is increased by 1 before printing the new value.
Output of the above code:
Let's replicate this behavior in Python with a mutable object. We will make use of a list and modify the first element of the list.
Output of the above code:
In this case, add() increases the first element's value by one. So, does this imply that Python has pointers? No! This was just possible because we have used a list that is mutable. Try the same code with a tuple. You will receive an error message.
Output of the above code:
The error was returned because the tuple is an immutable type. To achieve the desired result, experiment with other mutable objects. It's essential to understand that we're only simulating pointer behavior with mutable objects.
b) Using Custom Python Objects
In the previous example, we used dict to simulate a pointer in Python, but it can be difficult to remember all of the key names. We can use the Python custom class in place of the dictionary. Let's take a look at an example.
In the above code, the Pointer class is defined. In the _metrics member variable, this class used dict to store actual data. It will give our program flexibility. We can accomplish this as follows.
@property decorator was used. If you're not familiar with decorators, then study about decorators first. FunCalls and catPicture served will be accessible to the @property decorator. We will now create a Pointer class object.
These values must be increased here.
We've added two new methods: increment() and cat_pics (). We changed the values in the matrices dict by using these functions. We can change the class in the same way that we changed the pointer.
Real Pointers With ctypes
Note: This section requires advanced knowledge of C. You can skip this section if you are a beginner in the C language.
You can create real C-style pointers in Python by using the built-in ctypes module.
The actual purpose of using this is to invoke a function call in a C library that requires a pointer. Returning to the previous add one() C-function:
Again, this code increases the value of x by one. First, compile it into a shared object. Assuming the above file is in add.c, you could do the following with gcc:
The first command compiles the C source file into an add.o object. The second command creates a shared object called libadd1.so from the unlinked object file.
Your current directory should contain libadd1.so. You can use ctypes to load it into Python:
The shared object libadd1 is represented by the object that ctypes. CDLL code returns. You can access this shared object as if it were any other Python object because you defined add_one() in it. However, the function signature must be specified before calling the function. Python uses this to ensure that you pass the correct type to the function.
In this case, the function signature is a pointer to an integer. You can specify this by using the following code in ctypes:
In the above code, you're changing the function signature to match what C expects. If you tried to call this code with the wrong type, you'd get a nice warning rather than undefined behavior:
Python throws an error, stating that add_one() expects a pointer rather than just an integer. Fortunately, ctypes supports passing pointers to these functions. To begin, create a C-style integer:
The above code generates a C-style integer x with the value 0. The byref() function in ctypes allows you to pass a variable by reference.
Note: Passing a variable by reference differs from passing a variable by value. When you pass by reference, you are passing the reference to the original variable, and any changes will be reflected in the original variable. Passing by value creates a duplicate of the original variable, and any changes are not reflected in the original.
This can be used to call add_one():
Your integer has been increased by one. You have used real pointers in Python successfully.
Learn more
Conclusion
-
Pointers are variables that store the location of other variables. It is a data type that contains the addresses of other data types.
-
When we create a variable or an object in a programming language, we save it to a specific CPU address. When we output the data, it is drawn from that address. Pointers are used to store addresses and manage memory.
-
Python does not have a concept of pointers. Python does not support pointers. Perhaps because of the difficulty level of pointers, which goes against the Zen of Python.
-
Python prioritizes simplicity over speed. Pointers can be implemented in Python using other mutable objects.
Explore Scaler Topics Python Tutorial and enhance your Python skills with Reading Tracks and Challenges.