Variáveis e Tipos de Dados

Variáveis e Tipos de Dados

Tipagem dinâmica quer dizer que o tipo mora no valor, não no nome — e isso muda o que uma variável é. Os cinco tipos fundamentais, a conversão explícita que Python exige, por que 0.1 mais 0.2 não dá 0.3 em lugar nenhum e o detalhe do módulo decimal que decide se a correção funciona ou não.
Python

• • 11 min de leitura

Todo programa manipula informação. Para isso, a linguagem precisa de uma forma de armazenar e identificar dados na memória do computador. Em Python, esse mecanismo é simples e direto — mas esconde decisões de design que vale entender desde o início.

O que é uma Variável?

Uma variável é um nome que aponta para um valor armazenado na memória. Em Python, você não precisa declarar o tipo da variável antecipadamente — basta atribuir um valor:

nome = "Ricardo"
idade = 35
altura = 1.78
ativo = True

Python descobre o tipo automaticamente. Isso se chama tipagem dinâmica — o tipo está associado ao valor, não ao nome da variável.

Diferente de Java ou C, onde você escreveria:

// Java
String nome = "Ricardo";
int idade = 35;

Em Python, o nome da variável é apenas um rótulo. Você pode inclusive reatribuí-lo a um tipo diferente (embora isso seja má prática):

x = 10
x = "agora sou texto"  # válido, mas evite

Tipos de Dados Fundamentais

int — Números inteiros

ano = 2024
populacao = 215000000
temperatura_negativa = -15

print(type(ano))  # <class 'int'>

Python suporta inteiros de tamanho arbitrário — não há limite de bits como em C ou Java.

numero_gigante = 10 ** 100  # 1 googol — funciona perfeitamente

float — Números decimais

preco = 49.90
pi = 3.14159
gravidade = -9.8

print(type(preco))  # <class 'float'>

Atenção ao problema clássico de ponto flutuante:

>>> 0.1 + 0.2
0.30000000000000004

Isso não é bug do Python — é uma limitação do padrão IEEE 754, presente em praticamente todas as linguagens. Para cálculos financeiros, use o módulo decimal — e repare nas aspas, porque elas são a parte que importa:

from decimal import Decimal

resultado = Decimal("0.1") + Decimal("0.2")
print(resultado)  # 0.3

str — Texto

Strings podem ser delimitadas por aspas simples ou duplas — o resultado é idêntico:

saudacao = "Olá, mundo!"
linguagem = 'Python'

Para textos com múltiplas linhas, use três aspas:

mensagem = """
Linha 1
Linha 2
Linha 3
"""

Strings em Python são imutáveis — uma vez criadas, não podem ser alteradas. Operações sobre strings sempre geram uma nova string.

nome = "ricardo"
print(nome.upper())       # RICARDO
print(nome.capitalize())  # Ricardo
print(len(nome))          # 7
print(nome[0])            # r
print(nome[-1])           # o

bool — Booleanos

ligado = True
desligado = False

print(type(ligado))  # <class 'bool'>

Em Python, True e False são escritos com inicial maiúscula. Booleanos são subclasse de int — True vale 1 e False vale 0:

>>> True + True
2
>>> False * 100
0

NoneType — Ausência de valor

None representa a ausência de valor. É usado quando uma variável existe mas ainda não tem conteúdo definido:

resultado = None

if resultado is None:
    print("Ainda não foi calculado.")

Conversão entre Tipos

Python não converte tipos automaticamente (exceto em casos muito específicos). A conversão explícita é feita com funções:

# str para int
numero = int("42")
print(numero + 8)  # 50

# int para str
texto = str(100)
print("Valor: " + texto)  # Valor: 100

# str para float
preco = float("19.99")
print(preco * 2)  # 39.98

# int para float
x = float(5)
print(x)  # 5.0

Tentar converter um valor inválido gera um erro:

int("abc")  # ValueError: invalid literal for int()

Verificando o Tipo de uma Variável

x = 3.14
print(type(x))          # <class 'float'>
print(isinstance(x, float))   # True
print(isinstance(x, int))     # False

Boas Práticas na Nomeação de Variáveis

Python adota o estilo snake_case para variáveis e funções — palavras em minúsculo separadas por underscore:

# Correto
nome_completo = "Ricardo Matos"
total_de_alunos = 120
preco_unitario = 9.99

# Evite
NomeCompleto = "Ricardo Matos"   # reservado para classes
nomecompleto = "Ricardo Matos"   # difícil de ler
n = "Ricardo Matos"              # pouco descritivo

Nomes de variáveis não podem começar com número e não podem ser palavras reservadas da linguagem (if, for, class, return, etc.).

O ponto que sustenta tudo o que veio nesta parte é a frase que parece apenas didática: em Python o nome é um rótulo, e o tipo mora no valor. Daí decorre a conversão explícita que a linguagem exige, decorre o isinstance ser mais confiável que comparar o resultado de type, e decorre a surpresa de dois nomes apontarem para a mesma lista. Quem entende a variável como uma caixa que guarda um valor vai tropeçar nas três coisas; quem a entende como uma etiqueta colada num objeto não tropeça em nenhuma.

A aritmética de ponto flutuante merece a mesma atenção. O 0.30000000000000004 não é defeito do Python nem curiosidade de sala de aula: é o que acontece quando se guarda um número decimal em base dois, e vale para toda linguagem que usa IEEE 754. O decimal resolve, desde que o valor entre como texto — construir um Decimal a partir de um float apenas carrega o erro para dentro do módulo que deveria evitá-lo.

Fontes e leituras recomendadas

Exercícios

Exercício 1

O artigo recomenda o módulo decimal para cálculo financeiro e mostra Decimal("0.1") + Decimal("0.2"). Um colega escreve Decimal(0.1) + Decimal(0.2), sem as aspas, e reclama que "o decimal também erra". Ele está certo?

Ver resposta

✓ Resposta: O módulo está certo; a construção é que está errada. Com aspas, o Decimal recebe o texto "0.1" e representa exatamente um décimo. Sem aspas, o que chega ao construtor já é um float — o literal 0.1 foi convertido para binário antes de o decimal ver qualquer coisa, e o que ele recebe é o valor que de fato existe na memória. Dá para ver a olho nu: Decimal(0.1) imprime 0.1000000000000000055511151231257827021181583404541015625. O módulo está sendo fiel, reproduzindo o erro que já vinha pronto, e Decimal(0.1) + Decimal(0.2) devolve 0.3000000000000000166533453694. A regra é curta e vale para todo sistema que lida com dinheiro: um valor decimal nunca deve passar por float em nenhum ponto do caminho. Ele entra como texto vindo do formulário, viaja como Decimal, e é gravado numa coluna DECIMAL do banco — nunca FLOAT nem DOUBLE. Se em algum trecho alguém escreve float(valor) para "facilitar uma conta", o erro entra ali e não sai mais. Vale saber também que a alternativa comum, guardar centavos como inteiro, funciona pelo mesmo motivo: inteiro em Python é exato e não tem limite de tamanho.

Exercício 2

O artigo afirma que os booleanos são subclasse de int, e mostra True + True resultando em 2. Isso parece uma curiosidade inofensiva. Descreva um caso em que essa herança produz um defeito real.

Ver resposta

✓ Resposta: O caso mais silencioso é o dicionário. Como True == 1 e os dois têm o mesmo hash, eles são a mesma chave: escrever {1: "um", True: "verdadeiro"} produz um dicionário de um único item, e {1: "a"}[True] devolve "a" sem erro nenhum. Num código que usa identificadores numéricos vindos de um banco e, em algum ponto, deixa passar um booleano — uma flag lida de uma coluna, o retorno de uma comparação —, o valor simplesmente sobrescreve o do identificador 1, e o sintoma aparece muito longe da causa. Há um segundo caso, este útil: sum([True, True, False]) devolve 2, e é por isso que sum(1 for x in lista if condicao(x)) e sum(condicao(x) for x in lista) contam a mesma coisa — comportamento em que muito código se apoia de propósito. E há a armadilha da verificação de tipo: isinstance(True, int) devolve True, então uma função que valida a entrada com isinstance(valor, int) aceita True como se fosse número. Se a distinção importa, o teste tem de ser type(valor) is int, que é justamente o caso em que comparar o tipo diretamente é mais correto do que usar isinstance.

Exercício 3

O artigo insiste que "o nome da variável é apenas um rótulo". Você escreve a = [1, 2], depois b = a, e em seguida b.append(3). O que acontece com a, e por que o mesmo raciocínio não vale para x = 10 seguido de y = x?

Ver resposta

✓ Resposta: a passa a valer [1, 2, 3]. A atribuição b = a não copiou a lista: colou um segundo rótulo no mesmo objeto, e a is b é True. O append altera esse objeto no lugar, e quem estiver olhando por qualquer um dos nomes vê a mudança. Com x = 10 e y = x os dois nomes também apontam para o mesmo objeto, mas inteiro é imutável — não existe operação que altere o número 10 no lugar. Ao escrever y += 1, o que acontece não é modificar; é criar o objeto 11 e reapontar y para ele, deixando x intocado. Ou seja: a regra é a mesma para tudo, e é a mutabilidade do objeto que muda o que se observa. Em Python, imutáveis são int, float, str, bool, tuple e frozenset; mutáveis são list, dict, set e a maioria dos objetos que você mesmo define. A consequência prática mais cara aparece quando se passa uma lista para uma função: ela recebe o mesmo objeto, e qualquer alteração feita lá dentro é visível fora. Quem quer trabalhar sobre uma cópia precisa pedir por ela — b = a.copy() para um nível, copy.deepcopy(a) quando há estrutura aninhada.

Exercício 4

O artigo destaca que Python tem inteiros de tamanho arbitrário e mostra 10 ** 100 funcionando. Isso significa que Python não perde precisão com números grandes?

Ver resposta

✓ Resposta: Não — a garantia vale para int, e só. O 10 ** 100 é exato, com os 101 dígitos todos corretos, e 10 ** 100 + 1 é de fato diferente de 10 ** 100. Já 1e100 é um float, com os mesmos 64 bits e a mesma precisão limitada de sempre: 1e100 + 1 == 1e100 devolve True, porque o 1 é pequeno demais para aparecer na representação. E as duas formas nem sequer são o mesmo número — 1e100 == 10 ** 100 devolve False. O detalhe que pega na prática é que basta um ponto flutuante na expressão para o resultado inteiro virar float e perder a exatidão: a divisão / sempre devolve float, mesmo quando o resultado é redondo, e é por isso que existe a divisão inteira //. Duas outras conversões enganam pelo mesmo motivo. int(42.9) devolve 42, porque trunca em vez de arredondar. E round não faz o arredondamento da escola: usa a regra do dígito par, então round(2.5) é 2 e round(3.5) é 4 — perfeitamente correto segundo o padrão, e uma surpresa garantida para quem confere uma soma na mão.

Exercício 5

Você lê uma planilha exportada em CSV e converte a coluna de quantidade com int(valor). O código funciona com a maior parte dos arquivos e levanta ValueError em alguns. Quais são as causas prováveis e como tratá-las?

Ver resposta

✓ Resposta: Há quatro causas comuns, e só uma delas é "dado inválido". A primeira é o decimal disfarçado: int("42.0") levanta ValueError, mesmo o valor sendo inteiro — a função aceita a representação de um inteiro, não a de um número que por acaso é redondo. Planilha exporta quantidade como 42.0 com frequência, e a correção é int(float(valor)), ciente de que isso trunca. A segunda é o campo vazio: célula em branco chega como "", que também é ValueError e normalmente deveria virar None ou zero, conforme a regra do negócio — decisão que precisa ser tomada, não deixada para o acaso. A terceira é formatação numérica: separador de milhar ("1.234" ou "1,234") e símbolo de moeda vêm colados no número, e aqui mora uma armadilha regional séria, porque "1.234" significa mil e duzentos e trinta e quatro numa planilha em português e um vírgula duzentos e trinta e quatro numa em inglês — adivinhar dá erro por um fator de mil. A quarta é o caractere invisível: espaço em volta o int tolera sozinho, mas BOM no começo do arquivo e espaço não separável vindo da web, não. O tratamento certo não é envolver tudo num try que engole a exceção: é normalizar o que se sabe normalizar, e registrar qual linha e qual valor falharam, porque numa importação de milhares de registros a mensagem invalid literal for int() sem o contexto não ajuda ninguém a consertar a planilha.

Comentários

Mais em Python

Strings em Profundidade: métodos, formatação e expressões regulares
Strings em Profundidade: métodos, formatação e expressões regulares

Texto é o dado que mais chega torto. Além do repertório de métodos, formatação…

Banco de Dados com SQLite e SQLAlchemy
Banco de Dados com SQLite e SQLAlchemy

SQLite com sqlite3 e SQLAlchemy 2.0 em Python, do CRUD ao repositório e às…

Concorrência: threads, processos e async/await
Concorrência: threads, processos e async/await

Threads, processos e asyncio resolvem problemas diferentes, e escolher errado…