Aula 16: Métodos de Classe e Estáticos

Aprofundando nos comportamentos das classes em Python.

O Que Você Vai Aprender

Nesta aula, você mergulhará em dois conceitos importantes da Programação Orientada a Objetos em Python: os métodos de classe e os métodos estáticos. Você entenderá suas diferenças, quando e por que usá-los para escrever código mais organizado e eficiente.

🏭 Métodos de Classe (@classmethod)

Compreender métodos que operam na classe em vez de uma instância, úteis para construtores alternativos.

🛠️ Métodos Estáticos (@staticmethod)

Aprender sobre funções de utilidade dentro de uma classe que não precisam de acesso à instância nem à classe.

🆚 Quando Usar Cada Um

Diferenciar e decidir qual tipo de método é mais apropriado para cada cenário.

@classmethod: Operando na Classe

Um método de classe é decorado com `@classmethod`. Ele recebe a própria classe (convencionalmente chamada `cls`) como seu primeiro argumento, em vez da instância (`self`). Isso permite que o método de classe opere nos atributos da classe e possa criar novas instâncias da classe.

Uso Principal: Fábricas de Objetos

São comumente usados como construtores alternativos (ou "métodos de fábrica"), permitindo que você crie instâncias da classe de diferentes maneiras ou a partir de diferentes fontes de dados.

# Exemplo de @classmethod em Python

class Pessoa:
    # Atributo de classe
    total_pessoas = 0

    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
        Pessoa.total_pessoas += 1 # Incrementa o contador de pessoas

    def mostrar_info(self):
        return f"Nome: {self.nome}, Idade: {self.idade}"

    @classmethod
    def from_string_nascimento(cls, nome, ano_nascimento):
        """
        Método de classe para criar uma Pessoa a partir do ano de nascimento.
        'cls' refere-se à classe Pessoa.
        """
        ano_atual = 2025 # Simulando o ano atual
        idade = ano_atual - ano_nascimento
        return cls(nome, idade) # Retorna uma nova instância da classe 'cls'

# Uso:
# pessoa1 = Pessoa("Alice", 30)
# print(pessoa1.mostrar_info()) # Saída: Nome: Alice, Idade: 30

# Criando uma pessoa usando o método de classe
# pessoa2 = Pessoa.from_string_nascimento("Bob", 1990)
# print(pessoa2.mostrar_info()) # Saída: Nome: Bob, Idade: 35 (se ano_atual = 2025)

# print(f"Total de pessoas criadas: {Pessoa.total_pessoas}") # Saída: 2

@staticmethod: Funções de Utilidade

Um método estático é decorado com `@staticmethod`. Ele se comporta como uma função regular, mas está logicamente agrupado dentro de uma classe. Ele não recebe nem `self` (a instância) nem `cls` (a classe) como seu primeiro argumento.

Uso Principal: Funções Auxiliares

É usado para funções de utilidade que têm uma relação lógica com a classe, mas não precisam de acesso a nenhum estado da instância ou da classe. Pense nelas como funções globais que você decidiu colocar dentro de uma classe para melhor organização.

# Exemplo de @staticmethod em Python

class Matematica:
    @staticmethod
    def somar(a, b):
        """
        Método estático para somar dois números.
        Não precisa de 'self' nem 'cls'.
        """
        return a + b

    @staticmethod
    def eh_par(numero):
        """
        Método estático para verificar se um número é par.
        """
        return numero % 2 == 0

# Uso (não precisa de uma instância da classe):
# resultado_soma = Matematica.somar(5, 3)
# print(f"A soma é: {resultado_soma}") # Saída: A soma é: 8

# eh_par_5 = Matematica.eh_par(5)
# eh_par_4 = Matematica.eh_par(4)
# print(f"5 é par? {eh_par_5}") # Saída: 5 é par? False
# print(f"4 é par? {eh_par_4}") # Saída: 4 é par? True

Comparação: Quando Usar Cada Um

A escolha entre um método de instância, um método de classe e um método estático depende do que o método precisa acessar e de sua finalidade.

Método de Instância (Comum)

  • Recebe `self` (a instância).
  • Opera nos atributos da instância.
  • Ações específicas daquele objeto.
  • Ex: `pessoa.mostrar_info()`

Método de Classe (`@classmethod`)

  • Recebe `cls` (a classe).
  • Opera em atributos da classe ou cria novas instâncias.
  • Ações relacionadas à classe como um todo ou construtores alternativos.
  • Ex: `Pessoa.from_string_nascimento()`

Método Estático (`@staticmethod`)

  • Não recebe `self` nem `cls`.
  • Não acessa o estado da instância ou da classe.
  • Funções de utilidade que pertencem logicamente à classe, mas poderiam ser funções separadas.
  • Ex: `Matematica.somar()`

Dica: Se você precisa acessar os dados da *instância*, use um método de instância. Se você precisa acessar os dados da *classe* ou criar uma nova instância, use um método de classe. Se a função não precisa de nenhum deles, use um método estático para agrupamento lógico.

Simulador Interativo

Explore o comportamento dos métodos de classe e estáticos na prática com este simulador. As classes JavaScript abaixo imitam o comportamento das classes Python para demonstração.

Código Python das Classes Simuladas

# Classe Pessoa (para @classmethod)
class Pessoa:
    total_pessoas = 0

    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
        Pessoa.total_pessoas += 1

    def mostrar_info(self):
        return f"Nome: {self.nome}, Idade: {self.idade}"

    @classmethod
    def from_string_nascimento(cls, nome, ano_nascimento):
        ano_atual = new Date().getFullYear() # Adaptado para JS
        idade = ano_atual - ano_nascimento
        return cls(nome, idade)

# Classe Matematica (para @staticmethod)
class Matematica:
    @staticmethod
    def somar(a, b):
        return a + b

    @staticmethod
    def eh_par(numero):
        return numero % 2 == 0

Simulador de `Pessoa` (Método de Classe)

Crie instâncias de `Pessoa` usando o construtor normal e o método de classe.

Criar Pessoa (Construtor)

Criar Pessoa (`from_string_nascimento`)

Total de Pessoas Criadas: 0

Simulador de `Matematica` (Método Estático)

Use os métodos estáticos da classe `Matematica`.

Método Estático: `somar()`

Método Estático: `eh_par()`

Resultados do simulador aparecerão aqui.

Desafios para Continuar

Coloque seus conhecimentos em prática com estes desafios no seu ambiente Python.

  • 1. Classe `Produto` com `@classmethod`

    Crie uma classe `Produto` com atributos `nome`, `preco` e `quantidade_estoque`. Adicione um `@classmethod` chamado `from_dict(cls, dados_produto)` que recebe um dicionário (ex: `{'nome': 'Laptop', 'preco': 1200.0, 'estoque': 5}`) e retorna uma nova instância de `Produto`. Implemente também um método de instância `valor_total_estoque()`.

    # Exemplo de Estrutura:
    class Produto:
        def __init__(self, nome, preco, quantidade_estoque):
            self.nome = nome
            self.preco = preco
            self.quantidade_estoque = quantidade_estoque
    
        def valor_total_estoque(self):
            return self.preco * self.quantidade_estoque
    
        @classmethod
        def from_dict(cls, dados_produto):
            return cls(dados_produto['nome'], dados_produto['preco'], dados_produto['estoque'])
    
    # Teste:
    # dados = {'nome': 'Smartphone', 'preco': 800.0, 'estoque': 10}
    # meu_produto = Produto.from_dict(dados)
    # print(f"{meu_produto.nome}: R${meu_produto.valor_total_estoque():.2f} em estoque.")
                                
  • 2. Classe `ConversorTemperatura` com `@staticmethod`

    Crie uma classe `ConversorTemperatura`. Dentro dela, implemente dois `@staticmethod`:

    • `celsius_para_fahrenheit(celsius)`: Converte Celsius para Fahrenheit (F = C * 9/5 + 32).
    • `fahrenheit_para_celsius(fahrenheit)`: Converte Fahrenheit para Celsius (C = (F - 32) * 5/9).

    Você não precisará criar instâncias desta classe para usar os métodos.

    # Exemplo de Estrutura:
    class ConversorTemperatura:
        @staticmethod
        def celsius_para_fahrenheit(celsius):
            return (celsius * 9/5) + 32
    
        @staticmethod
        def fahrenheit_para_celsius(fahrenheit):
            return (fahrenheit - 32) * 5/9
    
    # Teste:
    # temp_f = ConversorTemperatura.celsius_para_fahrenheit(25)
    # print(f"25°C são {temp_f}°F")
    # temp_c = ConversorTemperatura.fahrenheit_para_celsius(77)
    # print(f"77°F são {temp_c}°C")