Pular para o conteúdo

Como utilizar ponteiros em Python?

CodeMDD.io

Ponteiros em Python: Qual é o Ponto?

Por que o Python não tem ponteiros?

A verdade é que eu não sei. Ponteiros em Python poderiam existir nativamente? Provavelmente, mas os ponteiros parecem ir contra o Zen do Python. Ponteiros encorajam mudanças implícitas em vez de explícitas. Muitas vezes, eles são complexos em vez de simples, especialmente para iniciantes. Além disso, eles tendem a criar situações perigosas, como ler de uma seção da memória que você não deveria.

O Python tende a abstrair os detalhes de implementação, como endereços de memória, dos usuários. O Python geralmente se concentra na usabilidade em vez da velocidade. Como resultado, ponteiros em Python não fazem muito sentido. Mas não se preocupe, o Python oferece alguns dos benefícios de usar ponteiros por padrão.

Entender ponteiros em Python requer uma breve discussão sobre os detalhes de implementação do Python. Especificamente, você precisa entender:

  1. Objetos imutáveis ​​vs mutáveis
  2. Variáveishttps://codemdd.io/nomes em Python

Segure seus endereços de memória e vamos começar.

Objetos em Python

Em Python, tudo é um objeto. Para provar isso, você pode abrir um REPL e explorar usando a função isinstance():

>>> isinstance(1, object)
True
>>> isinstance(list(), object)
True
>>> isinstance(True, object)
True
>>> def foo():
... pass
...
>>> isinstance(foo, object)
True

Esse código mostra que tudo em Python é um objeto, inclusive inteiros, listas, booleanos e funções. Esses objetos têm métodos e atributos, e você pode descobrir quais são eles usando a função dir():

>>> dir(1)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__']
>>> dir(list())
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> dir(True)
['__and__', '__bool__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__']
>>> dir(foo)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

Esses objetos têm métodos e atributos que você pode usar para interagir com eles. Por exemplo, você pode usar o método append() para adicionar um elemento a uma lista, ou o atributo __doc__ para obter a documentação de um objeto.

Objetos imutáveis ​​vs mutáveis

Em Python, os objetos podem ser classificados em dois grupos: imutáveis e mutáveis.

Objetos imutáveis ​​são aqueles que não podem ser modificados após serem criados. Por exemplo, inteiros, strings e tuplas são objetos imutáveis. Isso significa que, uma vez que você cria um objeto imutável, não pode alterar seu valor. Você pode apenas criar um novo objeto com um valor diferente. Por exemplo:

>>> a = 2
>>> b = a
>>> a += 1
>>> a
3
>>> b
2

Nesse exemplo, a é uma variável que aponta para o objeto imutável 2. Quando atribuímos a a b, estamos apenas fazendo b apontar para o mesmo objeto. No entanto, quando modificamos a variável a adicionando 1, estamos criando um novo objeto 3. Isso não afeta o objeto originalmente apontado por b, que permanece como 2.

Objetos mutáveis, por outro lado, podem ser modificados após serem criados. Por exemplo, listas, conjuntos e dicionários são objetos mutáveis. Isso significa que você pode alterar o valor desses objetos sem criar um novo objeto. Por exemplo:

>>> x = [1, 2, 3]
>>> y = x
>>> x.append(4)
>>> x
[1, 2, 3, 4]
>>> y
[1, 2, 3, 4]

Nesse exemplo, x e y são variáveis que apontam para o objeto mutável [1, 2, 3]. Quando atribuímos x a y, estamos fazendo y apontar para o mesmo objeto. Portanto, quando modificamos a lista x adicionando o número 4, essa modificação é refletida na lista y também.

A distinção entre objetos imutáveis ​​e mutáveis ​​é importante porque ela afeta como Python lida com variáveis e nomes. Vamos ver isso em mais detalhes na próxima seção.

Entendendo variáveis

Em C, uma variável é um local na memória que contém um valor. Por exemplo:

int x = 10;

Nesse código, criamos uma variável chamada x que contém o valor 10. Podemos também criar um ponteiro que aponta para o endereço de memória dessa variável:

int* ptr;
ptr = &x;

Aqui, ptr é um ponteiro que aponta para o endereço de memória de x. Podemos acessar o valor de x indiretamente usando o ponteiro:

*ptr = 20;

Agora, x contém o valor 20.

Em contraste, em Python, as variáveis não têm um valor diretamente atribuído a elas. Em vez disso, as variáveis são nomes que apontam para os objetos. Por exemplo:

x = 10

Nesse código, x é um nome que aponta para o objeto 10. Podemos verificar o endereço de memória desse objeto usando a função id():

>>> id(x)
140731018918144

Em Python, os nomes são apenas referências para os objetos e não contêm diretamente um valor. Podemos ter várias variáveishttps://codemdd.io/nomes apontando para o mesmo objeto:

x = 10
y = x

Aqui, tanto x quanto y são nomes que apontam para o objeto 10. Modificar o valor de x também afeta o valor de y:

x = 20
print(y) # Output: 20

Nesse exemplo, x deixou de apontar para o objeto 10 e passou a apontar para o objeto 20. No entanto, como y ainda está apontando para o objeto 10, quando imprimimos y, o valor que obtemos é 20.

Além disso, ao usar operações como atribuição, passagem de argumentos ou retorno de valores de função, o Python lida com os nomes e objetos de maneira transparente para você:

def foo(a):
a += 1
x = 10
foo(x)
print(x) # Output: 10

Nesse exemplo, a função foo() recebe o nome x, que aponta para o objeto 10. Dentro da função, o nome a é criado e passa a apontar para o mesmo objeto. No entanto, quando incrementamos a em 1, estamos criando um novo objeto 11 e fazendo a apontar para ele. Isso não afeta o valor original de x, que permanece como 10.

Simulando ponteiros em Python

Embora Python não tenha ponteiros como C, você ainda pode simular o comportamento dos ponteiros em certas situações. Aqui estão algumas maneiras de fazer isso:

Usando tipos mutáveis como ponteiros

Você pode usar tipos mutáveis, como listas, conjuntos ou dicionários, para simular o comportamento de ponteiros em Python. Por exemplo:

x = [1, 2, 3]
y = x
x.append(4)
print(y) # Output: [1, 2, 3, 4]

Nesse exemplo, x e y são variáveis que apontam para a lista [1, 2, 3]. Quando adicionamos o número 4 à lista x, essa modificação também é refletida na lista y.

Usando objetos Python

Outra maneira de simular ponteiros em Python é usar objetos Python personalizados. Por exemplo, você pode criar uma classe Pointer que contém uma referência a um objeto e permite modificar esse objeto por meio de um método. Veja um exemplo simples:

class Pointer:
def __init__(self, value):
self.value = value
def set_value(self, new_value):
self.value = new_value

Com essa classe, você pode criar um objeto Pointer que contém uma referência a um objeto e modificar esse objeto usando o método set_value():

x = Pointer(10)
y = x
x.set_value(20)
print(y.value) # Output: 20

Nesse exemplo, x e y são variáveis que apontam para o objeto Pointer(10), que contém uma referência ao objeto 10. Quando chamamos o método set_value() em x e passamos o valor 20, estamos alterando o valor do objeto para 20. Como y aponta para o mesmo objeto Pointer, quando imprimimos y.value, obtemos o valor 20.

Ponteiros reais com ctypes

Se você realmente precisa de ponteiros para interagir com código C existente, o módulo ctypes do Python permite trabalhar com ponteiros reais. Com ctypes, você pode manipular diretamente os endereços de memória e interagir com funções C. No entanto, essa abordagem é mais avançada e requer conhecimento de C e da biblioteca ctypes.

Conclusão

Ponteiros podem ser uma ferramenta poderosa para aumentar a eficiência do código, mas também podem ser difíceis de entender e gerar erros de gerenciamento de memória. Embora o Python não tenha ponteiros nativos, você pode simular ponteiros usando tipos mutáveis ou objetos Python personalizados. No entanto, tenha cuidado ao manipular ponteiros, pois eles podem levar a comportamentos inesperados e bugs difíceis de depurar.


Esse tutorial abordou os conceitos de ponteiros em Python, explicou por que o Python não possui ponteiros nativos e mostrou como simular ponteiros usando tipos mutáveis e objetos Python personalizados. Para um entendimento mais aprofundado, você pode explorar o módulo ctypes para trabalhar com ponteiros reais em Python.