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:
- Objetos imutáveis vs objetos mutáveis
- 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()
:
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:
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:
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:
Por exemplo, se você quiser declarar uma variável do tipo int
chamada x
, você escreveria:
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:
Por exemplo, se você quisesse atribuir o número inteiro 10 ao nome x
, você escreveria:
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:
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:
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:
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:
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
:
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