Métodos: instância, classe e estáticos
1. Introdução aos Métodos em Python
Em Python, métodos são funções definidas dentro do corpo de uma classe e que operam sobre os dados daquela classe. A principal diferença entre um método e uma função comum é que o método está associado a um objeto (instância) ou à própria classe, recebendo automaticamente uma referência implícita ao contexto onde foi chamado.
Python oferece três tipos distintos de métodos, cada um com seu propósito e comportamento específicos:
- Métodos de instância: operam sobre uma instância específica da classe
- Métodos de classe (
@classmethod): operam sobre a classe em si - Métodos estáticos (
@staticmethod): funcionam como funções utilitárias dentro do namespace da classe
Os decoradores @classmethod e @staticmethod são fundamentais para definir os dois últimos tipos, modificando o comportamento padrão de como o Python passa o primeiro argumento implícito.
2. Métodos de Instância
Os métodos de instância são o tipo mais comum e recebem self como primeiro parâmetro, que representa a instância específica do objeto.
class ContaBancaria:
def __init__(self, titular, saldo=0):
self.titular = titular
self.saldo = saldo
def depositar(self, valor):
"""Método de instância que modifica o estado do objeto."""
if valor > 0:
self.saldo += valor
return f"Depósito de R${valor:.2f} realizado. Novo saldo: R${self.saldo:.2f}"
return "Valor inválido para depósito"
def exibir_saldo(self):
"""Método de instância que retorna informações do estado atual."""
return f"Titular: {self.titular} | Saldo: R${self.saldo:.2f}"
# Chamada implícita (recomendada)
conta = ContaBancaria("Ana", 1000)
print(conta.depositar(500)) # Saída: Depósito de R$500.00 realizado. Novo saldo: R$1500.00
# Chamada explícita (equivalente)
print(ContaBancaria.exibir_saldo(conta)) # Saída: Titular: Ana | Saldo: R$1500.00
A chamada explícita Classe.metodo(instancia) demonstra que, internamente, o Python converte objeto.metodo() para Classe.metodo(objeto).
3. Métodos de Classe (@classmethod)
Métodos de classe recebem cls como primeiro parâmetro, que representa a classe em si (não a instância). São definidos com o decorador @classmethod.
class Pessoa:
especies = "Homo sapiens"
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
@classmethod
def criar_anonimo(cls, idade):
"""Factory method: cria uma pessoa sem nome definido."""
return cls("Anônimo", idade)
@classmethod
def obter_especie(cls):
"""Método de classe que acessa atributo da classe."""
return f"Espécie: {cls.especies}"
def apresentar(self):
return f"Olá, sou {self.nome} e tenho {self.idade} anos."
# Usando o construtor alternativo
pessoa1 = Pessoa.criar_anonimo(25)
print(pessoa1.apresentar()) # Saída: Olá, sou Anônimo e tenho 25 anos.
print(Pessoa.obter_especie()) # Saída: Espécie: Homo sapiens
A diferença fundamental é que cls permite acessar atributos e métodos da classe, além de criar novas instâncias (como no factory method acima).
4. Métodos Estáticos (@staticmethod)
Métodos estáticos não recebem nenhum parâmetro implícito (self ou cls). São definidos com o decorador @staticmethod e funcionam como funções comuns, mas organizadas dentro do namespace da classe.
class Calculadora:
@staticmethod
def validar_numero(valor):
"""Método estático: função utilitária de validação."""
return isinstance(valor, (int, float)) and valor > 0
@staticmethod
def converter_para_moeda(valor):
"""Método estático: formata valor como moeda."""
return f"R${valor:.2f}"
def __init__(self, valor_inicial):
if not Calculadora.validar_numero(valor_inicial):
raise ValueError("Valor inicial inválido")
self.valor = valor_inicial
# Uso sem instanciar a classe
print(Calculadora.validar_numero(100)) # Saída: True
print(Calculadora.converter_para_moeda(1500.5)) # Saída: R$1500.50
calc = Calculadora(200)
print(Calculadora.converter_para_moeda(calc.valor)) # Saída: R$200.00
Métodos estáticos são ideais para lógicas que pertencem conceitualmente à classe, mas não precisam acessar seus dados.
5. Comparação Prática e Casos de Uso
| Característica | Método de Instância | Método de Classe | Método Estático |
|---|---|---|---|
| Parâmetro implícito | self (instância) |
cls (classe) |
Nenhum |
| Acesso a atributos de instância | Sim | Não | Não |
| Acesso a atributos de classe | Sim (via self.__class__) |
Sim (via cls) |
Não |
| Pode ser chamado pela instância | Sim | Sim | Sim |
| Pode ser chamado pela classe | Sim (passando instância) | Sim | Sim |
Quando escolher cada tipo:
- Método de instância: operações que dependem ou modificam o estado do objeto específico.
- Método de classe: operações que envolvem a classe como um todo (factory methods, acesso a atributos de classe).
- Método estático: funções utilitárias relacionadas ao domínio da classe, mas sem dependência de estado.
Exemplo integrado:
class Pedido:
taxa_entrega = 5.0 # Atributo de classe
def __init__(self, cliente, itens):
self.cliente = cliente
self.itens = itens
self.total = sum(itens.values())
def aplicar_desconto(self, percentual):
"""Método de instância: modifica o estado do pedido."""
desconto = self.total * (percentual / 100)
self.total -= desconto
return f"Desconto de {percentual}% aplicado. Novo total: R${self.total:.2f}"
@classmethod
def atualizar_taxa(cls, nova_taxa):
"""Método de classe: altera atributo compartilhado."""
cls.taxa_entrega = nova_taxa
return f"Taxa de entrega atualizada para R${nova_taxa:.2f}"
@staticmethod
def validar_cupom(codigo):
"""Método estático: validação simples."""
return len(codigo) == 8 and codigo.isalnum()
pedido = Pedido("Carlos", {"pizza": 30.0, "refrigerante": 8.0})
print(pedido.aplicar_desconto(10)) # Saída: Desconto de 10% aplicado...
print(Pedido.atualizar_taxa(7.0)) # Saída: Taxa de entrega atualizada para R$7.00
print(Pedido.validar_cupom("DESC10")) # Saída: False
6. Comportamento em Herança e Polimorfismo
class Animal:
def __init__(self, nome):
self.nome = nome
def emitir_som(self):
"""Método de instância: será sobrescrito nas subclasses."""
return "Som genérico de animal"
@classmethod
def criar_por_tipo(cls, tipo, nome):
"""Método de classe: factory method polimórfico."""
if tipo == "cachorro":
return Cachorro(nome)
elif tipo == "gato":
return Gato(nome)
return cls(nome)
@staticmethod
def validar_idade(idade):
"""Método estático: herdado como função."""
return 0 <= idade <= 30
class Cachorro(Animal):
def emitir_som(self):
return "Au au!"
@classmethod
def criar_por_tipo(cls, tipo, nome):
# 'cls' aqui é Cachorro, não Animal
return cls(nome)
class Gato(Animal):
def emitir_som(self):
return "Miau!"
# Testando polimorfismo com métodos de instância
animais = [Cachorro("Rex"), Gato("Mimi")]
for animal in animais:
print(f"{animal.nome}: {animal.emitir_som()}")
# Método de classe respeita a subclasse
rex = Animal.criar_por_tipo("cachorro", "Rex")
print(type(rex).__name__) # Saída: Cachorro (cls aponta para subclasse)
# Método estático é herdado sem polimorfismo
print(Cachorro.validar_idade(5)) # Saída: True
Note que cls em métodos de classe aponta para a subclasse que chamou o método, permitindo polimorfismo. Já métodos estáticos são herdados como funções comuns, sem comportamento polimórfico.
7. Boas Práticas e Armadilhas Comuns
-
Não usar
@staticmethodquando um método de instância é mais adequado: se o método precisa acessarself, use método de instância. -
Evitar
@classmethodpara funcionalidades que seriam melhores como funções modulares: se o método não usaclspara nada relacionado à classe, considere transformá-lo em função no módulo. -
Cuidado com herança e métodos estáticos: como não são polimórficos, podem causar confusão se você espera que subclasses os sobrescrevam com comportamento diferente.
-
Documentação clara: sempre use docstrings para indicar a intenção do método:
class Produto:
@staticmethod
def calcular_imposto(valor):
"""
Calcula o imposto sobre o valor do produto.
Este método é estático pois não depende do estado
de nenhuma instância ou classe específica.
"""
return valor * 0.15
- Prefira métodos de classe para factory methods: eles são mais flexíveis que métodos estáticos para criar instâncias, especialmente em hierarquias de herança.
Referências
- Python Documentation: Class methods vs static methods — Documentação oficial sobre os decoradores
@classmethode@staticmethod. - Real Python: Python's Instance, Class, and Static Methods Demystified — Tutorial completo e detalhado sobre os três tipos de métodos em Python.
- GeeksforGeeks: Class method vs Static method in Python — Comparação prática entre métodos de classe e estáticos com exemplos.
- Programiz: Python @staticmethod and @classmethod — Explicação didática com exemplos de código para iniciantes.
- Python.org: Python Classes and Objects — Seção oficial do tutorial de Python sobre classes, métodos e herança.