Understanding the absence of pointers in Python

31

In some simple applications that I have had to write in C / C ++ I have seen the ease with which certain tasks are solved using pointers . Now, more interested in another language: Python, I have noticed the absence of this concept. What is the reason for this absence? Python being a very powerful and used language, then, what concept replaces it? Is it implicit in the types of data, in the assignments, in the instantiation of a class?

An extremely simple example would be that in C we can encode things like this:

#include <stdio.h>

int main(void) {
    // your code goes here
    int a = 5;
    int *b = &a;
    printf("a = %d; b = %d\n", a, *b); // (1)

    a = 6;
    printf("a = %d; b = %d\n", a, *b); // (2)
    return 0;
}
  

(1): a = 5; b = 5

     

(2): a = 6; b = 6

b points to the memory address of a , any modification in a can be observed when dereferencing b . Any assignment by indirection *b = <valor>; will modify a .

But, in Python:

a = 5
b = a
print "a = {0:d}; b = {1:d}".format(a,b) # (1)
b is a # resultado: True

a = 6
print "a = {0:d}; b = {1:d}".format(a,b) # (2)
b is a # resultado: False
  

(1): a = 5; b = 5

     

(2): a = 6; b = 5

At the beginning a and b refer to the same object. Then, when a is modified a new object is created; then, both refer to different objects and different values.

There is no way to do what in C in Python with this type of data, but it is possible to do something similar with mutable data types; however, it is only possible when we make internal modifications of the mutable data, for example: changing the value of an element of a list.

a = [1, 2]
b = a

print b is a # resultado: True

b.append(3)

print b is a # resultado: True

a = [4, 5]

print b is a # resultado: False
    
asked by OSjerick 04.12.2015 в 00:00
source

4 answers

33

Its absence is due to the fact that the explicit use of pointers is a characteristic of lower level languages such as C. High level languages such as Python avoid it with the purpose of making its use easier and more agile, as well as not have to know details of the data model.

That the Python programmer does not have to deal with the pointers does not mean that the interpreter does not use them. In fact, he uses them profusely implicitly.

In Python everything is an object created in dynamic memory (automatically maintained). When you call a function, the arguments are passed through their pointers. This is what is known as a call-by-object convention. Similarly, if you assign a = b , a what it saves is the pointer of the b object. So all the variables are pointers to objects, which are handled implicitly.

What must be differentiated is between immutable and mutable objects.

  • Immutable are numbers, strings or tuples. Assigning x = 2015 will create an integer object and x will point to it but the content of that object can not be modified. If you then assign x = 2016 what it will do internally it will create a new object with the new content.
  • Mutables are other objects such as dictionaries or lists. In this case, said objects may be modified. If you have v = [1] and then call v.append(2) , v will still point to the same object, but its content will have changed.

In summary, when executing this code:

x = 2015
y = x
x = 2016

print x
print y

v = [1]
w = v
v.append(2)

print v
print w

The result will be:

2016
2015
[1, 2]
[1, 2]
    
answered by 04.12.2015 / 01:56
source
7

In C, pointers usually meet three needs: referencing dynamically reserved structures, passing parameters to a by reference function, or iterating a collection.

In the case of Python, and the languages of objects with automatic memory in general, the variables fulfill the function of referencing dynamically created structures: one can create instances of the objects at any time.

In general, the objects are reserved in the dynamic memory of the processes, and the variables are references to them: almost almost that the references are abstractions of the pointers, with some more properties.

For this reason, the passage of parameters is always done by reference, so no pointers are needed for this.

Finally, in object languages there are iterator objects, which expose a higher level interface to traverse data collections.

Abstracting the details of the memory of the process is something sought after in the languages, and for all this is that the pointers are not necessary: by design .

    
answered by 04.12.2015 в 01:41
3

The response of @GuillermoRuiz seems excellent to me, but I would like to delve into some details about the mutability and immutability, which are often confusing at the beginning, but which are very clear if you bear in mind that they are all pointers .

Change items in a list

The fact that a list is " mutable " implies not only that we can add elements to it, but that we can change the saved values.

In reality, being purists, the list only contains "pointers" to the data in question. That is, a list like this:

a = [1, 2, 3]

It actually contains three pointers, each pointing to an integer, of respective values 1 , 2 and 3 . If we now change the first element:

a[0] = 100

When printing the list we will see:

>>> a
[100, 2, 3]

This does not mean that the first element in the list has been replaced by a 100 (that would be true in an array C), but that a new object of integer type has been created and that the pointer in the first element of the list that pointed to a 1 , now points to 100 . The previous 1 remains "no references" and will be removed from memory later by the garbage collector.

Copies of lists

On the other hand, the fact that the variables are actually pointers (or if you prefer, references) implies that the following assignment:

b = a

does not copy the elements of a , but simply assigns a copy of the pointer to b . a That is, a and b actually point to the same list. Therefore if we do:

b[0] = 50

is the same as if we had done a[0] = 50 .

To check if two variables "point" to the same data, Python offers the comparator is :

>>> a is b
True

If we do not want them to point to it, but to be a copy (in different places of memory), we can achieve it like this:

b = list(a)

or like this:

b = a[:]

In either of these two cases:

>>> a is b
False
>>> a == b
True

The operator == compares the elements of a with those of b , while is compares the identity of a with b , which in practice it consists of comparing which memory address each one refers to.

Lists as parameters

The above also explains why a function can modify elements of a list that it receives as a parameter:

def pon_cero(lista):
   lista[0] = 0

a = [1,2,3]
pon_cero(a)
print(a)
# [0, 2, 3]

In a tuple, being immutable, this can not be done.

Immutability, but not so much

But careful , the fact that a tuple is immutable only means that you can not change the value of tupla[i] to another value, but if tupla[i] were a list, the values in that list yes they could be changed:

tupla = ( [0,0], [1,1] )
# tupla[0] = [2,2] # No está permitido. Error
tupla[0][0] = 2
tupla[0][1] = 2    # No hay problema aqui en cambio
print(tupla)
([2,2], [1,1])
    
answered by 15.12.2018 в 23:53
0

It's a little botched, but if you really need 1 variable and a "pointer" you can do:

class mutable_obj:
    def __init__(self, x):
        self.x = x

a = mutable_obj(10)
b = a
a.x == b.x # True
a.x = 30
a.x == b.x # True
b.x = 86
a.x == b.x # True

I think it's unnecessary, likewise if someone knows a better way to say it! : D

    
answered by 17.07.2017 в 20:25