Pular para conteúdo

Tipos de Embutidos

⚠️ Este artigo é um rascunho e pode conteúdo ausente ou incompleto.

1. Introdução

Este texto é uma introdução aos tipos embutidos do Python. São tipos de variáveis que permitem armazenar e trabalhar com dados na linguagem. Tipos embutidos fazem parte da base do Python, ou seja, não é preciso instalar nenhuma biblioteca externa ou extensões da linguagem para poder trabalhar com os tipos embutidos. Além disso, uma grande parte dos tipos embutidos são tipos built-in: que estão disponíveis globalmente de qualquer lugar e possuem palavras reservadas na linguagem.

numero = int(65) # Pode ser usado sem importar nada no código

Diferente de outras linguagens de programação, Python não possui tipos primitivos da forma tradicional. Em Python, tudo é um objeto, incluindo os tipos mais básicos como int, float, bool e str. Por exemplo, o int, que representa um número inteiro em Python, ao invés de ser apenas um número bruto, é possível acessar métodos a partir da varável que está armazenando este tipo de dado. Isso garante uma maior abstração na linguagem, fazendo com que o programador não precise envelopar o valor bruto com um objeto que implementa estes métodos.

numero = 65
print(numero.to_bytes()) # Saída: b'A'
print(numero.bit_count()) # Saída: 2

Entender como trabalhar com os principais tipos embutidos do Python é importante para um maior controle sobre os dados com que se está lidando, evitando bugs inesperados e/ou dificuldade na hora de estruturar e implementar algorítimos. Um ótimo exemplo são os números de ponto flutuante, é fácil um desenvolvedor que não conhece o funcionamento deles na linguagem acabar corrompendo os dados ao manipula-los.

numero1 = 0.1 + 0.1 + 0.1
numero2 = 0.3

if numero1 == numero2:
    print("Os números são iguais.")
else:
    print("Os números não são iguais.") # O resultado será esse

Apenas olhando para o código, sem entender como o Python lida com esses números, parece estar tudo correto. Mas, na verdade, as duas variáveis acabam armazenando números com valores diferentes. Mais adiante, neste artigo, será abordado de forma aprofundada os números flutuantes e os principais tipos embutidos do Python para que o você seja capaz de analisar, entender e lidar com estas e outras situações similares.

2. Valor verdade dos objetos

Qualquer objeto em Python pode ser testado se é verdade ou falso. Por padrão, os objetos são considerados verdade quando submetidos a um teste de verdade utilizando operadores booleanos ou condições booleanas como if e while, exceto quando o método object.__bool__() retorna False ou o objeto possuí o método object.__len__() e ele retorna 0. Estes métodos fazem parte de um grupo especial de métodos da linguagem, comumente chamados de métodos especiais ou métodos dunder, são utilizados para customizar objetos.

numero = 2
print(numero.__bool__()) # Saída: True
print(numero.__len__())  # Saída: AttributeError
print(numero == 2)       # Saída: True
print(numero != 2)       # Saída: False
print(numero > 3)        # Saída: False
print(numero < 3)        # Saída: False
print(numero >= 3)       # Saída: True

Alguns dos objetos built-in que são considerados falso:

Tipo Exemplos
Constantes None e False
Valores numéricos 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
Sequências e coleções vazias '', [], (), {}, set(), range(0)

3. Tipos Numéricos

Python fornece três tipos numéricos built-in — funcionalidades built-in são recursos que não precisam de importação para serem utilizadas na linguagem e possuem nomes reservados: inteiros (int), números de ponto flutuante (float) e números complexos (complex). É importante destacar que os valores booleanos em Python são um subtipo (herança) built-in dos números inteiros, sendo o True equivalente a 1 e False equivalente a 0.

3.1. Construtores

Os construtores int(), float() e complex() podem ser utilizados para criar números de um tipo específico.

num_inteiro = int(1)
num_flutuante = float(1)
num_complexo = complex(1, 2)

Também é possível produzir estes números através de uma sintaxe literal.

num_inteiro = 1
num_flutuante = 1.0
num_flutuante = 1e-2
num_complexo = 1.0+2.0j
num_complexo = 1+2j

3.2. Precisão dos Números Flutuantes

Números de ponto flutuante possuem precisão limitada de acordo com a arquitetura do sistema onde o Python está sendo executado. Essa precisão é equivalente ao double da linguagem C, na qual o interpretador padrão do Python, o CPython, é desenvolvido.

Para curiosidade, Python utiliza a especificação IEEE 754 double precision (64 bits) para representar números de ponto flutuante. Essa especificação divide o número em três partes: 1 bit para o sinal, que indica se o número é positivo ou negativo; 11 bits para o expoente, que representa a magnitude do número; e 52 bits para a fração (também chamada de mantissa armazenada), que contém os dígitos significativos. Como todos os números normalizados em binário começam com 1., esse primeiro bit é implícito — não é armazenado, mas assumido pelo hardware durante os cálculos. Isso resulta em 53 bits de precisão efetiva, equivalente a aproximadamente 15-16 dígitos decimais significativos. No entanto, apenas os primeiros 15 dígitos são confiáveis, o 16º dígito pode estar incorreto devido a arredondamento, especialmente após operações aritméticas.

Essa especificação permite que diversas linguagens de programação possam representar os números de ponto flutuate de forma precisa. Mas com limitações, já que o hardware, a memória RAM, não consegue armazenar infinitas casas decimais ou números significativos infinitos de um número de ponsto flutuante.

É possível obter a precisão suportada pelo seu sistema executando este código:

import sys
print(sys.float_info)

A saída será algo como:

sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

O limite de precisão confiável de dígitos significativos do seu sistema será o valor exibido em dig=15. No caso do sistema qual foi executado o exemplo, que é um sistema 64bits, o máximo de números significativos de um número de ponto flutuante será de 15 dígitos. A contagem de significativos inclui todos os dígitos entre o primeiro e último não-zero, incluindo zeros intermediários.

Exemplos de números significativos:

Número Significativos Quantidade
1230,1230 1230123 7 significativos
0,0012301230 1230123 7 significativos
1230123,00 1230123 7 significativos

3.3. Problemas ao lidar com Números de Ponto Flutuante

Como vimos anteriormente, os números de ponto flutuante têm precisão limitada; e por conta disso, podem ter algumas características que podem ser problemáticas em operações aritiméticas e expressões condicionais. O exemplo abaixo ilustra bem uma situação comum em um código feito por um desenvolvedor que não conhece muito bem os números de ponto flutuante:

numero1 = 0.1 + 0.1 + 0.1
numero2 = 0.3

if numero1 == numero2:
    print("Os números são iguais.")
else:
    print("Os números não são iguais.")

Esse código cairá no resultado do else, pois o resultado da expressão aritimética da variável numero1 não resulta em 0.3, mas sim em 0.30000000000000004. E quando comparamos se esse número é igual a 0.3, resultará em False. Para lidar com esse tipo de problema com números de ponto flutuante o Python nos fornece a função math.isclose(). Essa função infere automaticamente se a diferença entre os dois números de ponto flutuante é irrelevante. O mesmo exemplo, mas agora corrigindo o erro anterior, ficaria assim:

import math

numero1 = 0.1 + 0.1 + 0.1
numero2 = 0.3

if math.isclose(numero1, numero2):
    print("Os números são iguais.")
else:
    print("Os números não são iguais.")

Um outro erro comum ao trabalhar com números flutuantes é exceder o limite de precisão durante uma operação aritimética, perdendo parte do dado no processo:

limite = 2**53 # Equivale a: 9007199254740992.0
print(f"{limite + 1.1:f}") # Saída: 9007199254740994.000000

Isso ocorre porque a mantissa de 53 bits equivale a aproximadamente 15.95 dígitos decimais. O 16º dígito pode estar correto em alguns casos, mas não é confiável. Neste exemplo, quando foram somados 1.1 ao limite, resultou em um número de 17 números significativos, que não pôde ser representado corretamente na memória, tendo seu valor corrompido até mesmo no 16º dígito.

Referências Bibliográficas