Tipos de métodos
Nesta aula, exploramos os três tipos de métodos em Python: métodos de instância, métodos de classe (@classmethod) e métodos estáticos (@staticmethod). Aprendemos suas diferenças, quando usar cada um e vimos exemplos práticos de implementação.
Em Python, métodos são funções definidas dentro de uma classe que operam em instâncias ou na própria classe. Dependendo de como são declarados, os métodos podem ter acesso a diferentes níveis de dados: instância, classe ou nenhum. Compreender essas diferenças é essencial para escrever código orientado a objetos limpo e eficiente.
Nesta aula, abordaremos os três tipos principais de métodos: métodos de instância (os mais comuns), métodos de classe (marcados com @classmethod) e métodos estáticos (marcados com @staticmethod). Veremos como cada um funciona, quando utilizá-los e exemplos práticos.
Métodos de instância
Métodos de instância são os métodos mais comuns em Python. Eles recebem como primeiro parâmetro self, que representa a instância atual da classe. Através de self, é possível acessar e modificar atributos da instância, bem como chamar outros métodos da instância.
Esses métodos são chamados a partir de uma instância da classe e podem acessar tanto atributos de instância quanto atributos de classe (através de self.__class__). São ideais para comportamentos que dependem do estado específico de cada objeto.
class Pessoa:
especie = "Homo sapiens"
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def apresentar(self):
return f"Olá, meu nome é {self.nome} e tenho {self.idade} anos."
def aniversario(self):
self.idade += 1
# Uso
p = Pessoa("Alice", 30)
print(p.apresentar()) # Olá, meu nome é Alice e tenho 30 anos.
p.aniversario()
print(p.idade) # 31No exemplo acima, apresentar e aniversario são métodos de instância. Eles usam self para acessar os atributos da instância (nome, idade).
@classmethod
Métodos de classe são declarados com o decorador @classmethod e recebem como primeiro parâmetro cls, que representa a própria classe (não uma instância). Eles podem acessar e modificar atributos de classe e chamar outros métodos de classe, mas não têm acesso a atributos de instância (a menos que recebam uma instância como argumento).
Métodos de classe são úteis para criar fábricas (factory methods) que retornam instâncias da classe usando lógica alternativa, ou para operações que envolvem a classe como um todo.
class Pessoa:
especie = "Homo sapiens"
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
@classmethod
def criar_anonimo(cls):
return cls("Anônimo", 0)
@classmethod
def mudar_especie(cls, nova_especie):
cls.especie = nova_especie
# Uso
p = Pessoa.criar_anonimo()
print(p.nome) # Anônimo
Pessoa.mudar_especie("Homo sapiens sapiens")
print(Pessoa.especie) # Homo sapiens sapiensNote que criar_anonimo usa cls para instanciar a classe, e mudar_especie altera o atributo de classe especie.
@staticmethod
Métodos estáticos são declarados com o decorador @staticmethod e não recebem nenhum parâmetro especial (self ou cls). Eles se comportam como funções normais, mas pertencem ao namespace da classe. Não podem acessar diretamente atributos de instância ou de classe (a menos que recebam explicitamente como argumentos).
Métodos estáticos são usados para funções utilitárias que estão relacionadas ao conceito da classe, mas não dependem do estado da instância ou da classe.
class Calculadora:
@staticmethod
def somar(a, b):
return a + b
@staticmethod
def subtrair(a, b):
return a - b
# Uso
resultado = Calculadora.somar(10, 5)
print(resultado) # 15
# Também pode ser chamado a partir de uma instância
calc = Calculadora()
print(calc.subtrair(10, 5)) # 5No exemplo, somar e subtrair são funções independentes que apenas realizam operações matemáticas, sem depender de nenhum estado interno.
Quando usar cada um
A escolha entre método de instância, método de classe e método estático depende do tipo de acesso necessário:
- Método de instância: Use quando o método precisa acessar ou modificar o estado de uma instância específica (atributos de instância). É o tipo mais comum.
- Método de classe: Use quando o método precisa acessar ou modificar atributos de classe, ou quando precisa criar instâncias da classe de forma alternativa (factory). Também útil para polimorfismo em herança.
- Método estático: Use quando o método não precisa de acesso a instância nem classe, mas faz sentido agrupar a função dentro da classe por questões de organização ou coesão. Exemplos: funções de validação, conversão, operações matemáticas.
Em geral, se você não precisa de self ou cls, opte por @staticmethod. Se precisa de cls (para acessar a classe), use @classmethod. Caso contrário, um método de instância comum é a escolha correta.
Boas práticas
- Não abuse de métodos estáticos se a função poderia ser uma função independente no módulo. Use-os apenas quando fizer sentido semântico pertencer à classe.
- Métodos de classe são úteis para implementar construtores alternativos (ex.:
from_string,from_dict). - Lembre-se de que métodos de classe podem ser sobrescritos em subclasses, enquanto métodos estáticos se comportam como funções regulares (não há polimorfismo).
- Evite usar
@staticmethodquando o método poderia ser um método de instância ou de classe – isso torna o código menos flexível.
Referências
- Documentação oficial de classes em Python
- Real Python: Instance, Class, and Static Methods
- GeeksforGeeks: Class method vs Static method
- Stack Overflow: Difference between @staticmethod and @classmethod
- Python Tips: Class vs Instance Variables
Exercícios
Crie uma classe
ContaBancariacom atributostitular(instância) esaldo(instância). Adicione métodos de instânciadepositaresacarque modifiquem o saldo.✓ Resposta:class ContaBancaria: def __init__(self, titular, saldo=0): self.titular = titular self.saldo = saldo def depositar(self, valor): self.saldo += valor def sacar(self, valor): if valor <= self.saldo: self.saldo -= valor else: print("Saldo insuficiente") # Teste conta = ContaBancaria("João", 100) conta.depositar(50) conta.sacar(30) print(conta.saldo) # 120Adicione um método de classe
criar_conta_vipà classeContaBancariaque cria uma conta com saldo inicial de 1000 e titular fornecido.✓ Resposta:class ContaBancaria: def __init__(self, titular, saldo=0): self.titular = titular self.saldo = saldo @classmethod def criar_conta_vip(cls, titular): return cls(titular, saldo=1000) # Teste conta_vip = ContaBancaria.criar_conta_vip("Maria") print(conta_vip.saldo) # 1000Crie um método estático
validar_cpfna classePessoaque recebe uma string e retorna True se tiver 11 dígitos, False caso contrário. Não precisa de validação real.✓ Resposta:class Pessoa: def __init__(self, nome, cpf): self.nome = nome self.cpf = cpf @staticmethod def validar_cpf(cpf): return len(cpf) == 11 and cpf.isdigit() # Teste print(Pessoa.validar_cpf("12345678901")) # True print(Pessoa.validar_cpf("123")) # FalseExplique por que o método
criar_conta_vipfoi implementado como método de classe em vez de método estático.✓ Resposta:Porque ele precisa criar uma nova instância da classe usandocls. Se fosse um método estático, teríamos que nomear explicitamente a classeContaBancaria, o que quebra a herança: se uma subclasse herdar deContaBancaria, o método estático ainda criaria objetos da classe pai, enquanto o método de classe usaclsque se refere à subclasse corretamente.Refatore o código abaixo para usar o tipo de método mais adequado: a classe
Matematicatem um métodocalcular_mediaque recebe uma lista de números e retorna a média. O método não usaselfnemcls.class Matematica: def calcular_media(self, numeros): return sum(numeros) / len(numeros)✓ Resposta:O método não usaselfnemcls, portanto deve ser um método estático. Refatoração:class Matematica: @staticmethod def calcular_media(numeros): return sum(numeros) / len(numeros)