Pular para o conteúdo

Como usar ponteiros em Python?

[

Ponteiros em Python: Qual é o ponto?

por Logan Jones intermediário python


Por que o Python não tem ponteiros?

O Python tende a abstrair as informações de implementação, tais como endereços de memória, de seus usuários. Comumente, o Python foca na usabilidade em vez de velocidade. Como resultado, os ponteiros no Python não fazem muito sentido. Mas não se preocupe, o Python, por padrão, oferece alguns dos benefícios do uso de ponteiros.

Entender os ponteiros no Python requer uma breve análise aos detalhes de implementação do Python. Especificamente, você precisará entender:

  1. Objetos imutáveis vs objetos mutáveis
  2. Variáveis/nomes em Python

Vamos lá, segure seus endereços de memória e vamos começar.


Objetos em Python

No Python, tudo é um objeto. Como prova disso, 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, até mesmo os tipos primitivos, como inteiros e booleanos. Porém, os objetos do Python diferem dos objetos em linguagens como C ou C++. Em C, quando você declara uma variável, um espaço de memória é alocado para armazenar o valor da variável. No Python, quando você cria um objeto, um espaço de memória é alocado para armazenar o objeto em si e outra variável (ou nome) é atribuída a esse objeto.

Em outras palavras, em Python, você não manipula objetos diretamente, mas sim manipula nomes que se referem a objetos. Esses nomes são capazes de se referir a diferentes objetos ao longo do tempo, tornando o gerenciamento de memória totalmente transparente para você.


Objetos imutáveis vs objetos mutáveis

É importante entender que, em Python, existem objetos mutáveis e objetos imutáveis. Objetos mutáveis podem ter seu valor alterado sem perder a identidade do objeto. Já objetos imutáveis, uma vez criados, não pode ser alterados. Em vez disso, cada vez que um objeto imutável é modificado, é criado um novo objeto com um novo valor.

Essa distinção é relevante porque ela afeta diretamente como o Python trata as atribuições de variáveis. Quando você atribui um objeto imutável a uma variável, na verdade, está criando uma nova variável que aponta para o mesmo objeto. Por exemplo:

>>> a = 10
>>> b = a
>>> a is b
True

Nesse caso, a e b são duas variáveis diferentes que apontam para o mesmo objeto 10. No entanto, se você atribuir um objeto mutável a uma variável, as coisas mudam:

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

Ao atribuir a lista x à variável y, ambas as variáveis apontam para o mesmo objeto. Portanto, qualquer alteração feita em uma das variáveis também será refletida na outra.


Entendendo variáveis

Antes de mergulharmos na simulação dos ponteiros em Python, é importante entender a diferença entre variáveis em C e nomes em Python.

Variáveis em C

Em C, uma variável é um espaço de memória que foi alocado para armazenar um valor. Quando você declara uma variável em C, você está basicamente pedindo à linguagem para alocar espaço suficiente na memória para o tipo de dado especificado.

A declaração de uma variável em C segue essa sintaxe:

<tipo_de_dado> <nome_da_variavel>;

Por exemplo, se você quiser declarar uma variável do tipo int chamada x, você escreveria:

int x;

Agora você tem um espaço de memória chamado x que pode armazenar um número inteiro.


Nomes em Python

Python é diferente. Quando você atribui um valor a um nome em Python, você está na verdade criando uma referência a um objeto em algum lugar da memória.

A atribuição de um valor a um nome em Python segue esta sintaxe:

<nome_da_variavel> = <valor>

Por exemplo, se você quisesse atribuir o número inteiro 10 ao nome x, você escreveria:

x = 10

Aqui, x é um nome que se refere a um objeto que contém o valor 10. O objeto 10 é um objeto imutável do tipo int.


Uma nota sobre objetos internos em Python

Em algumas situações, você pode querer comparar dois objetos para ver se eles são o mesmo objeto, em vez de comparar seus valores. Você pode fazer isso usando o operador is, que testa a igualdade dos objetos:

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x == y
True
>>> x is y
False

No exemplo acima, embora as duas listas x e y tenham valores idênticos, elas são dois objetos diferentes na memória. Portanto, o operador == retorna True, enquanto o operador is retorna False.

É importante notar que objetos internos podem ser uma exceção a essa regra. Por serem objetos internos, eles sempre apontam para o mesmo objeto na memória. Portanto, ao comparar objetos internos, o operador is pode retornar True mesmo que os objetos não tenham sido criados na mesma linha de código:

>>> a = None
>>> b = None
>>> a is b
True

No exemplo acima, tanto a quanto b são referências ao mesmo objeto interno None, por isso o operador is retorna True.


Simulando ponteiros em Python

Embora o Python não tenha ponteiros nativos como C ou C++, você pode simular o comportamento de ponteiros de outras maneiras. Vamos ver algumas delas:

Usando tipos mutáveis como ponteiros

Uma das formas de simular ponteiros em Python é utilizando tipos mutáveis, como listas ou dicionários. Em Python, quando você atribui um objeto mutável a uma variável, você está, na verdade, criando uma referência a esse objeto. Se você passar essa variável para outra função, ela terá uma referência ao mesmo objeto. Qualquer modificação feita no objeto será refletida em ambas as variáveis.

Considere o exemplo a seguir:

def modify_list(lst):
lst.append(4)
x = []
modify_list(x)
print(x)

Nesse exemplo, a lista vazia x é passada para a função modify_list(). Dentro da função, a lista é modificada, adicionando o valor 4 a ela. Quando a função retorna, o conteúdo da lista é impresso na tela, resultando em [4].

Note que não estamos manipulando objetos diretamente, mas sim manipulando nomes que se referem a objetos. Essa é uma forma de simular ponteiros em Python.

Usando objetos do Python

Outra forma de simular ponteiros em Python é utilizando objetos Python. Os objetos em Python são representações abstratas de ponteiros. Eles possuem um tipo próprio e uma referência interna para um objeto real na memória.

Um exemplo disso é a classe weakref do módulo weakref. Esse módulo permite que você crie referências fracas para objetos em Python. Referências fracas não impedem o garbage collector de remover o objeto da memória quando não há mais referências fortes para ele.

Veja o exemplo a seguir:

import weakref
class Data:
def __init__(self, value):
self.value = value
x = Data(10)
y = weakref.ref(x)
print(y().value)

Nesse exemplo, a classe Data possui um atributo value que armazena o valor. Depois, criamos uma instância dessa classe chamada x e uma referência fraca y para esse objeto. Quando chamamos a função y(), ela retorna uma referência para o objeto. Podemos acessar o atributo value através dessa referência.

É importante notar que, como y é uma referência fraca, o objeto x pode ser removido da memória e y() pode retornar None se o objeto tiver sido coletado pelo garbage collector.


Ponteiros reais com ctypes

Uma maneira de usar ponteiros em Python é por meio do módulo ctypes. O ctypes é um módulo que permite a chamada de funções e a criação de estruturas de dados em bibliotecas compartilhadas. Ele também oferece suporte ao uso de ponteiros.

Aqui está um exemplo que mostra como usar ponteiros com o ctypes:

import ctypes
# Define a estrutura da sua variável
class MyData(ctypes.Structure):
_fields_ = [("value", ctypes.c_int)]
# Cria uma instância da estrutura
data = MyData()
data.value = 10
# Cria um ponteiro para a instância da estrutura
pointer = ctypes.pointer(data)
# Acessa o valor através do ponteiro
print(pointer.contents.value)

Nesse exemplo, nós primeiro definimos a estrutura da variável usando a classe Structure do módulo ctypes. A estrutura tem um único campo value do tipo int. Depois, criamos uma instância da estrutura chamada data.

Em seguida, usamos a função pointer() para criar um ponteiro para a instância data. Por fim, podemos acessar o valor da instância através do ponteiro usando o atributo contents.


Conclusão

Embora o Python não tenha ponteiros nativos como C ou C++, você pode simular o comportamento de ponteiros usando outros recursos da linguagem, como o uso de tipos mutáveis, objetos Python ou módulos como o ctypes. Essas técnicas permitem que você manipule referências a objetos e crie comportamentos similares aos dos ponteiros.

No entanto, é importante lembrar que Python é uma linguagem de alto nível que se preocupa com a facilidade de uso e abstração de detalhes de implementação. Simular ponteiros em Python pode não ser uma prática comum ou necessária em muitos cenários. Entender e usar corretamente os recursos da linguagem é fundamental para escrever um código Python limpo e eficiente.


Escrito por Logan Jones intermediário python