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 como implementá-los com exemplos práticos.
Em Python, os métodos definidos dentro de uma classe podem ser classificados em três categorias principais: métodos de instância, métodos de classe e métodos estáticos. Cada um possui comportamentos e finalidades distintas, e a escolha adequada impacta a clareza e a manutenibilidade do código. Nesta aula, vamos explorar cada tipo, entender suas assinaturas e decoradores, e discutir cenários práticos para sua utilização.
Compreender essas diferenças é fundamental para projetar classes coesas e seguir boas práticas de programação orientada a objetos. Vamos começar com o tipo mais comum: os métodos de instância.
Métodos de instância
Métodos de instância são funções definidas dentro de uma classe que operam sobre uma instância específica (objeto) da classe. Eles recebem o parâmetro self como primeiro argumento, que se refere à própria instância. Através de self, o método pode acessar e modificar atributos da instância e chamar outros métodos da mesma instância.
A maioria dos métodos que você escreve em classes Python são métodos de instância. Eles são chamados diretamente no objeto, como objeto.metodo(). Exemplo:
class Pessoa:
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."
p = Pessoa("Ana", 30)
print(p.apresentar()) # Olá, meu nome é Ana e tenho 30 anos.No exemplo, apresentar é um método de instância que usa self.nome e self.idade. Cada instância possui seus próprios valores, e o método opera sobre eles.
@classmethod
Métodos de classe são definidos com o decorador @classmethod e recebem o parâmetro cls (referência à classe, não à instância) como primeiro argumento. Eles podem ser chamados tanto na classe quanto em uma instância, mas operam sobre a classe em si, não sobre instâncias específicas.
Métodos de classe são úteis para criar métodos de fábrica (factory methods) que retornam novas instâncias da classe, ou para acessar/modificar atributos da classe (variáveis de classe). Exemplo:
class Pessoa:
especie = "Humano"
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
p = Pessoa.criar_anonimo()
print(p.nome) # Anônimo
print(Pessoa.especie) # Humano
Pessoa.mudar_especie("Homo sapiens")
print(Pessoa.especie) # Homo sapiensNote que criar_anonimo usa cls para instanciar a classe (cls(...)), o que permite que subclasses herdem corretamente o método.
@staticmethod
Métodos estáticos são definidos com o decorador @staticmethod e não recebem nem self nem cls automaticamente. Eles são funções comuns dentro do namespace da classe, sem acesso a atributos da instância ou da classe (a menos que sejam passados como argumentos).
Métodos estáticos são usados quando uma função está relacionada à classe mas não depende de seu estado. Eles ajudam na organização do código, agrupando funções utilitárias junto à classe. Exemplo:
class Matematica:
@staticmethod
def somar(a, b):
return a + b
@staticmethod
def pi():
return 3.14159
print(Matematica.somar(2, 3)) # 5
print(Matematica.pi()) # 3.14159Métodos estáticos podem ser chamados na classe ou em instâncias, mas não têm acesso a self ou cls. São equivalentes a funções definidas fora da classe, mas colocadas dentro por questões de organização.
Quando usar cada um
A escolha entre os três tipos depende do que o método precisa acessar:
- Métodos de instância: Use quando o método precisa acessar ou modificar o estado da instância (atributos de instância). É o tipo mais comum.
- Métodos de classe (@classmethod): Use quando o método precisa acessar ou modificar atributos da classe, ou quando você precisa de um método de fábrica que retorne uma instância da classe (ou de uma subclasse). Exemplos: construtores alternativos, manipulação de variáveis de classe.
- Métodos estáticos (@staticmethod): Use quando o método não precisa acessar nem a instância nem a classe, mas está logicamente relacionado à classe. Exemplos: funções auxiliares, validações que não dependem de estado.
Boa prática: se você não usa self ou cls no método, considere se ele deveria ser um método estático ou uma função independente. Se a função for usada apenas dentro da classe, um método estático pode ser apropriado. Caso contrário, talvez seja melhor defini-la fora.
Boas práticas e observações finais
- Métodos de classe são frequentemente usados como construtores alternativos. Por exemplo, datetime.datetime.fromtimestamp() é um método de classe que cria um objeto datetime a partir de um timestamp.
- Métodos estáticos podem ser substituídos por funções modulares, mas mantê-los dentro da classe pode melhorar a coesão do código.
- Lembre-se de que métodos de classe e estáticos podem ser chamados tanto na classe quanto em instâncias, mas chamar um método estático em uma instância pode ser confuso; prefira chamá-los pela classe.
Referências
- Documentação oficial: Classes Python
- Documentação do decorador @classmethod
- Documentação do decorador @staticmethod
- Real Python: Instance, Class, and Static Methods
- GeeksforGeeks: Class vs Static Method
- Stack Overflow: @classmethod vs @staticmethod
Exercícios
- Implemente uma classe
ContaBancariacom métodos de instânciadepositar,sacareexibir_saldo. Use atributos de instânciatitularesaldo. - Crie um método de classe
criar_conta_poupancana classeContaBancariaque retorna uma nova conta com saldo inicial de R$ 50,00. - Adicione um método estático
validar_cpfque recebe uma string e retornaTruese o CPF tiver 11 dígitos (apenas simulação). - Explique por que o método
criar_conta_poupancadeve ser um método de classe e não um método estático. - Crie uma classe
Calculadoracom um método estáticosomae um método de classeversaoque retorna uma string com a versão da calculadora (ex.: "Calculadora v1.0").
class ContaBancaria:
def __init__(self, titular, saldo=0):
self.titular = titular
self.saldo = saldo
def depositar(self, valor):
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 sacar(self, valor):
if 0 < valor <= self.saldo:
self.saldo -= valor
return f"Saque de R$ {valor:.2f} realizado. Novo saldo: R$ {self.saldo:.2f}"
return "Saldo insuficiente ou valor inválido."
def exibir_saldo(self):
return f"Saldo atual: R$ {self.saldo:.2f}"@classmethod
def criar_conta_poupanca(cls, titular):
return cls(titular, saldo=50.0)@staticmethod
def validar_cpf(cpf):
# Simulação: verifica se tem 11 dígitos
return len(cpf) == 11 and cpf.isdigit()cls). Se fosse estático, não teria acesso à classe para instanciar, e teria que referenciar ContaBancaria diretamente, o que quebraria a herança. Usando @classmethod, subclasses podem herdar o método e criar instâncias delas mesmas.class Calculadora:
versao_app = "1.0"
@staticmethod
def soma(a, b):
return a + b
@classmethod
def versao(cls):
return f"Calculadora v{cls.versao_app}"