Curso de Python / Criar classes, instâncias e métodos com self
Uma classe define dados e comportamentos relacionados, e uma instância é um objeto individual criado a partir dela. Use essa organização quando o estado e suas regras precisarem ficar juntos; uma função simples ainda pode ser suficiente para um cálculo sem estado próprio.
Relacionar a classe às instâncias e aos métodos
A classe Expense deste exemplo representa uma despesa com nome e valor. Uma chamada como Expense("lunch", 8000) cria uma instância e inicializa seus atributos. Outra chamada pode criar uma despesa diferente, sem substituir a primeira. As duas usam os comportamentos definidos na mesma classe.
Um método é uma função definida na classe que pode receber uma instância e trabalhar com seus atributos. self é o nome convencional do parâmetro que recebe essa instância; não é uma palavra reservada. Ao chamar first.describe() na forma usual, Python fornece o objeto first para esse parâmetro automaticamente.
__init__() prepara o estado inicial depois da criação do objeto. Aqui ele recebe nome e valor, verifica uma regra e armazena os dois atributos. Não passe self manualmente ao chamar Expense(...). A documentação sobre objetos de classe e de instância apresenta essa relação.
Executar duas instâncias de despesa
Salve o programa completo em classes_demo.py, com UTF-8, numa pasta de prática. Execute python classes_demo.py no terminal com o ambiente virtual selecionado, se houver. Em macOS ou Ubuntu sem ambiente virtual, use python3 classes_demo.py quando necessário. Não há dependências externas.
class Expense:
def __init__(self, name, amount):
if amount < 0:
raise ValueError("negative amount")
self.name = name
self.amount = amount
def describe(self):
return f"{self.name}: {self.amount:,} KRW"
first = Expense("lunch", 8000)
second = Expense("bus", 2500)
print(first.describe())
print(second.describe())
print(first.amount + second.amount)
A saída esperada é:
lunch: 8,000 KRW
bus: 2,500 KRW
10500
lunch e bus são nomes de exemplo, correspondentes a almoço e ônibus. A primeira instância guarda o valor 8000 e a segunda, 2500. Cada chamada a describe() usa os atributos do objeto que a recebeu e devolve uma string; o código externo a imprime. A última linha soma diretamente os dois atributos numéricos e produz 10500.
O especificador :, separa milhares por vírgula, e o texto KRW identifica won sul-coreano. Esse método apenas apresenta o valor com um rótulo; não converte moeda nem aplica regras de formatação brasileira. A diferença entre retornar uma descrição e imprimi-la segue a mesma lógica das funções com valores de retorno.
Acompanhar o estado e os limites da validação
No estado inicial do programa, os nomes e atributos têm esta relação:
| Nome | Instância | Atributos iniciais |
|---|---|---|
first |
Primeiro objeto Expense. |
name="lunch", amount=8000. |
second |
Outro objeto Expense. |
name="bus", amount=2500. |
Alterar first.amount não altera second.amount neste exemplo: são atributos de instâncias distintas. Já atribuir alias = first daria um segundo nome ao mesmo objeto, e mudanças feitas por esse alias seriam vistas por first. A prática de aliases e objetos mutáveis ajuda a separar um novo objeto de apenas um novo nome.
A condição em __init__() só rejeita um valor quando a comparação amount < 0 é verdadeira. Um texto numérico não é convertido automaticamente e causa TypeError na comparação com zero. Um booleano ou um número fracionário não negativo pode passar por essa verificação. Se a aplicação exigir inteiros comuns, nomes não vazios ou outra escala, precisa de verificações adicionais.
A verificação inicial também não controla toda alteração posterior. Uma atribuição direta como first.amount = -1 é aceita pela implementação apresentada; o método de descrição poderá mostrar o valor negativo. Não confunda “foi validado na inicialização” com “não pode se tornar inválido”. A referência de inicialização de instâncias explica o papel de __init__.
Testar independência, chamadas e alterações indevidas
- Depois da criação de
seconde antes das impressões, acrescentefirst.amount = 9000. Execute e confiralunch: 9,000 KRW,bus: 2,500 KRWe 11500. A segunda despesa deve permanecer em 2500. Retire a linha para restaurar o estado inicial. - Troque somente 8000 por -1 na criação de
first. A inicialização deve levantarValueError: negative amountantes das impressões. Restaure 8000 após o teste. - Ao fim do programa, acrescente
print(Expense.describe(first) == first.describe()). O resultado adicional deve serTrue: na primeira forma, você fornece explicitamente a instância; na segunda, ela é associada ao método. Remova a impressão depois de comparar. - Para observar o limite de validação, acrescente temporariamente
first.amount = -1depois das duas criações. A execução continua e a primeira descrição mostra o valor negativo. Retire essa atribuição e confirme novamente as três linhas originais.
Uma grafia errada como first.amout = 9000 pode criar um atributo novo, sem corrigir first.amount. Verifique os atributos usados e os resultados observados, em vez de supor que toda atribuição bem-sucedida atingiu o campo pretendido.
Mantenha a responsabilidade de Expense focada nos dados e nas regras da despesa. Acrescentar entrada interativa, armazenamento em arquivo e envio pela rede à mesma classe pode tornar seus usos e testes mais difíceis. Recursos como propriedades, dataclasses e anotações de tipo podem ajudar em problemas específicos, mas não substituem automaticamente as verificações que o programa precisa impor em tempo de execução.




