Como otimizar suas classes em Python?

Jair Henrique
gb.tech
Published in
3 min readMar 18, 2022
Imagem de uma cobra

Quando criamos um objeto de uma classe python, os atributos desse objeto são armazenados em um dicionários chamado __dict__. Esse dicionário é utilizado para definir e obter os atributos do objeto e também te dá a liberdade de criar novos atributos dinamicamente a partir do objeto.

Para exemplificar, vou criar uma classe chamada Pessoa que irá receber 2 parâmetros, nome e idade. Após criar a instância da classe, vou adicionar um novo parâmetro para essa instância chamado sobrenome.

Exemplo de uma classe padrão em Python

Essa é uma das coisas bem legais em python. Pode parecer estranho para quem não está acostumado com linguagens dinâmicas, mas essa liberdade permite você fazer coisas incríveis com metaprogramação.

Dicionários são uma ferramenta extremamente poderosa em python, mas ela tem um custo e é importante você entender os problemas que eles podem causar.

Os problemas do dicionário

  • Dicionários precisam de memória! Imagina que sua aplicação precise criar milhões de objetos, você vai consumir bastante RAM!
  • Dicionários são hash maps e o pior cenário de complexidade para fazer um get/set em uma propriedade é O(n).

Como otimizar nossas classes então?

A resposta para essa questão está na palavra __slots__!

O que são __slots__? Segue uma tradução livre da documentação do python.

Essa variável de classe pode receber uma string, iterável ou uma sequência de strings com nomes de variáveis usados por instâncias. __slots__ reserva espaço para as variáveis declaradas e impede a criação automática de __dict__ e __weakref__ para cada instância

Vamos criar agora a classe pessoa definindo os __slots__. Perceba que quando nós definimos os __slots__ a instância da classe não possuiu mais o __dict__ e se tentamos criar um novo atributo para a classe também recebemos um erro como resposta.

Exemplo de uma class em Python utilizando slots

Vamos ver na prática, os dois benefícios que conseguimos ao definir os __slots__!

Primeiro benefício: Velocidade⚡️

Como dito anteriormente, o dicionário é um hash map e seu pior cenário de acesso a um atribruto é O(n). Quando temos __slots__ o python faz uso de uma lista para mapear os atributos. Uma lista possui uma complexidade O(1) no seu pior cenário de acesso a um atributo, em poucas palavras isso quer dizer que a lista é mais performática que o dict.

Nesse link é possível comparar o tempo de complexidade da implementação padrão de python dos tipos dict, list, set e collections.deque

Teste de velocidade comparando uma classe com e sem slots

Nesse exemplo podemos observar que uma instância de uma classe com __slots__ ao realizar a tarefa de atribuir, acessar e excluir um atributo de uma instância é mais rápida!

Segundo benefício: Economia de memória 🪶

Para medir o consumo de memória vamos utilizar uma biblioteca chamada pympler. O python possui nativamente uma função chamada getsizeof do módulo sys, mas ele não inclui na sua contabilização os objetos de referenciados ou seja, o __dict__ é um objeto refenciado e seria ignorado pelo getsizeof().

Teste de uso de memória comparando uma classe com e sem slots

Nesse exemplo conseguimos um ganho de mais de 50% de espaço em memória!!!!

Conclusão

Da noite para o dia você não precisa adicionar __slots__ em todas as suas classes, em alguns cenários a liberdade que o __dict__ te proporciona é mais vantajoso que o ganho de memória e velocidade!

Analise seu cenário antes de fazer qualquer otimização prematura!

Obrigado!

--

--