Trilha de aprendizado · Nível 12 · Tutorial 9

Definir contratos nominais com classes abstratas

Criar interfaces baseadas em herança explícita com ABC e abstractmethod, distinguindo suas garantias das oferecidas por Protocol.

  • Nível: Avançado
  • Duração: 20 min
  • 7 passos
Definir contratos nominais com classes abstratas

O que você vai percorrer

  1. Reconhecer a adesão explícita a um contrato Entenda por que um contrato nominal depende de pertencer declaradamente a uma hierarquia de classes. 2 min
  2. Declarar operações abstratas e bloquear instâncias incompletas Use ABC e @abstractmethod para declarar operações obrigatórias e identificar quais classes ainda não podem ser instanciadas. 3 min
  3. Implementar subclasses concretas substituíveis Concretize uma operação abstrata em subclasses, preserve sua assinatura e use diferentes implementações por meio do mesmo tipo base. 3 min
  4. Compartilhar lógica concreta na classe abstrata Reutilize uma operação em lote na classe abstrata sem abrir mão da implementação específica exigida de cada formatador. 3 min
  5. Separar instanciação, assinaturas e comportamento Diferencie o bloqueio de classes abstratas, a análise estática de assinaturas e os testes de comportamento. 4 min
  6. Escolher entre ABC e Protocol Compare contratos nominais e estruturais para decidir quando a herança explícita é requisito e quando basta oferecer operações compatíveis. 2 min
  7. Aplicação final: completar e verificar o contrato Complete uma nova implementação de formatador e reúna evidências distintas para concretização, compatibilidade estática e comportamento. 4 min

O que você vai aprender

  • Declarar uma classe abstrata com operações obrigatórias.
  • Implementar subclasses concretas que atendam ao contrato anotado.
  • Distinguir a proibição de instanciar uma classe incompleta da verificação de assinaturas.
  • Justificar a escolha entre um contrato nominal e um contrato estrutural.

Antes de começar

  • Definir interfaces estruturais com Protocol
  • Especializar classes com herança e super
  • Decoradores de métodos.

Passo 1 de 7

Reconhecer a adesão explícita a um contrato

Entenda por que um contrato nominal depende de pertencer declaradamente a uma hierarquia de classes.

Uma família declarada de formatadores

Contrato nominal

Em um contrato nominal, a compatibilidade começa pela relação declarada entre classes: uma implementação precisa pertencer à família do tipo base, por herança direta ou indireta.

Imagine Formatador como a base de uma família. FormatadorMaiusculo e FormatadorTitulo fazem parte dessa família porque a estendem. Por isso, podem ser tratadas como formatadores em pontos do código que exigem Formatador.

Quem pertence à família?

A seta de herança é a evidência de adesão ao contrato nominal.

Diagrama de herança: Formatador no topo; FormatadorMaiusculo e FormatadorTitulo abaixo dele; FormatadorComEmoji abaixo de FormatadorTitulo; uma classe externa chamada Copiador fora da hierarquia, sem seta de ligação.

Herança direta ou indireta liga uma classe ao contrato nominal; sem essa ligação, métodos parecidos não bastam.

Mesmo método, vínculo diferente

Exemplo

A anotação exige a família

class Formatador:
    def formatar(self, texto: str) -> str:
        return texto

class FormatadorMaiusculo(Formatador):
    def formatar(self, texto: str) -> str:
        return texto.upper()

class Copiador:
    def formatar(self, texto: str) -> str:
        return texto.upper()


def exibir(formatador: Formatador, texto: str) -> None:
    print(formatador.formatar(texto))

FormatadorMaiusculo adere nominalmente: sua declaração inclui Formatador. Já Copiador tem um método de mesmo nome e resultado semelhante, mas não declarou participação nessa hierarquia.

Dica

O que a anotação expressa

Ao anotar formatador: Formatador, você registra uma decisão de projeto: esse ponto aceita objetos da família Formatador. Em código tipado, um verificador estático pode apontar o uso de Copiador como incompatível por ele não herdar da base.

A anotação, sozinha, não impede automaticamente uma chamada em tempo de execução nem valida os argumentos recebidos.

Identifique a adesão nominal

Qual classe satisfaz o contrato nominal?

Considere esta hierarquia:

class Formatador:
    def formatar(self, texto: str) -> str:
        return texto

class FormatadorTitulo(Formatador):
    pass

class FormatadorComEmoji(FormatadorTitulo):
    pass

class Copiador:
    def formatar(self, texto: str) -> str:
        return texto

Uma função está anotada para receber Formatador. Qual classe abaixo participa desse contrato por herança indireta?

Passo 2 de 7

Declarar operações abstratas e bloquear instâncias incompletas

Use ABC e @abstractmethod para declarar operações obrigatórias e identificar quais classes ainda não podem ser instanciadas.

Uma base com operação obrigatória

Declare o contrato abstrato

No contrato nominal, a classe base faz parte da família por herança explícita. Para exigir uma operação antes de permitir instâncias, importe ABC e abstractmethod do módulo abc, herde de ABC e decore o método obrigatório.

O corpo ... indica que esta declaração não fornece uma implementação utilizável. A assinatura anotada documenta a operação que as classes da família deverão concretizar.

Base abstrata de formatadores

python
from abc import ABC, abstractmethod


class FormatadorTexto(ABC):
    @abstractmethod
    def formatar(self, texto: str) -> str:
        ...

O bloqueio vem da operação pendente

Diagrama de uma classe base FormatadorTexto marcada como abstrata, ligada a duas subclasses ainda incompletas; ícones de cadeado indicam que nenhuma delas pode ser instanciada enquanto formatar estiver pendente.

Uma operação abstrata pendente bloqueia a criação de instâncias, mas não a declaração de subclasses.

Declarar não é instanciar

Subclasses incompletas são permitidas

Python permite declarar uma subclasse mesmo que ela ainda não complete todas as operações abstratas. O impedimento aparece apenas ao criar uma instância: a base abstrata e qualquer descendente com métodos abstratos pendentes produzem TypeError.

Neste momento, não é preciso implementar a subclasse: observe que ela pode existir no código, mas não pode ser construída.

Tentativas de instanciação

python
class FormatadorParcial(FormatadorTexto):
    pass

# FormatadorTexto()
# TypeError: Can't instantiate abstract class FormatadorTexto
# with abstract method formatar

# FormatadorParcial()
# TypeError: Can't instantiate abstract class FormatadorParcial
# with abstract method formatar

Atenção

O que ABC garante — e o que não garante

O TypeError informa que ainda há operações abstratas pendentes. Ele não é uma validação dos argumentos recebidos em tempo de execução e não prova que uma futura implementação produzirá a formatação correta.

O decorador é a regra

ABC sozinho não torna uma classe abstrata

Herdar de ABC, por si só, não bloqueia instâncias. O bloqueio depende de pelo menos um método marcado com @abstractmethod que permaneça pendente.

Também não confunda um método abstrato com um método comum que lança NotImplementedError. Sem o decorador, Python permite instanciar a classe; a falha só ocorrerá se aquela operação for chamada.

Dois casos diferentes

python
from abc import ABC


class BaseSemObrigacao(ABC):
    pass


class BaseComFalhaTardia:
    def formatar(self, texto: str) -> str:
        raise NotImplementedError("Implemente formatar")


BaseSemObrigacao()  # permitido
BaseComFalhaTardia()  # permitido
# BaseComFalhaTardia().formatar("Olá")  # levanta NotImplementedError

Complete o decorador

Para exigir que formatar seja concretizado antes de criar instâncias, escreva @____ antes do método.

Preveja a instância permitida

Verifique a regra

A expressão BaseSemObrigacao() gera TypeError apenas porque BaseSemObrigacao herda de ABC.

Passo 3 de 7

Implementar subclasses concretas substituíveis

Concretize uma operação abstrata em subclasses, preserve sua assinatura e use diferentes implementações por meio do mesmo tipo base.

Concretizar o contrato na subclasse

Uma implementação torna a subclasse concreta

A classe base Formatador exige o método formatar. Uma subclasse se torna instanciável quando fornece esse método com os mesmos parâmetros e com retorno compatível.

Neste exemplo, Maiusculas implementa formatar(texto: str) -> str. Como não resta nenhuma operação abstrata pendente, Maiusculas("!") pode ser criada normalmente.

Da obrigação à instância concreta

Diagrama mostrando Formatador como classe abstrata com a operação formatar, ligada a Maiusculas como subclasse concreta que implementa a operação e pode ser instanciada.

A implementação de formatar remove a pendência abstrata da subclasse.

Contrato, implementação e consumidor

Salve como formatadores.py. A função consumidora recebe o tipo base, não uma subclasse específica.

python
from abc import ABC, abstractmethod


class Formatador(ABC):
    def __init__(self, sufixo: str) -> None:
        self.sufixo = sufixo

    @abstractmethod
    def formatar(self, texto: str) -> str:
        pass


class Maiusculas(Formatador):
    def formatar(self, texto: str) -> str:
        return texto.upper() + self.sufixo


def exibir(formatador: Formatador, texto: str) -> None:
    print(formatador.formatar(texto))


exibir(Maiusculas("!"), "Python")
# Saída: PYTHON!

Herança também pode concluir a obrigação

Uma classe descendente pode herdar a implementação

A implementação não precisa ser repetida em toda classe descendente. Se uma classe intermediária já concretizou formatar, suas descendentes também não têm essa pendência — salvo se introduzirem outra operação abstrata.

ComColchetes é concreta porque implementa o contrato. ComColchetesDuplos herda essa implementação e, por isso, também pode ser instanciada.

Implementação herdada

Acrescente estas classes ao mesmo arquivo, antes das chamadas finais:

python
class ComColchetes(Formatador):
    def formatar(self, texto: str) -> str:
        return f"[{texto}]{self.sufixo}"


class ComColchetesDuplos(ComColchetes):
    pass


exibir(ComColchetes("."), "aviso")
exibir(ComColchetesDuplos("."), "aviso")
# Saída nas duas chamadas:
# [aviso].

Dica

O tipo aceito é o da família

A anotação formatador: Formatador permite que a função receba instâncias de subclasses dessa hierarquia. A chamada de formatar usa a implementação disponível na instância recebida.

Prática: adicione outra transformação

Implemente e execute

No seu computador, acrescente Minusculas ao arquivo. Ela deve herdar de Formatador, aceitar o mesmo sufixo do inicializador herdado e implementar formatar(self, texto: str) -> str.

O resultado de exibir(Minusculas("."), "PyThOn") deve ser python.. Depois, execute o arquivo com python formatadores.py e confirme que tanto Maiusculas quanto Minusculas funcionam pelo mesmo consumidor.

Código a acrescentar

Insira a classe antes das chamadas e acrescente a chamada final.

python
class Minusculas(Formatador):
    def formatar(self, texto: str) -> str:
        # Substitua esta linha pela transformação necessária.
        pass


exibir(Minusculas("."), "PyThOn")
# Saída esperada: python.

Relate sua verificação

Quais foram as saídas de Maiusculas e Minusculas? Explique por que ambas são instanciáveis e por que exibir pode receber as duas.

Escreva pelo menos 80 caracteres (0/80).

Passo 4 de 7

Compartilhar lógica concreta na classe abstrata

Reutilize uma operação em lote na classe abstrata sem abrir mão da implementação específica exigida de cada formatador.

Uma base pode ter regra comum e ponto de variação

Abstração não significa ausência de código

Uma classe que herda de ABC pode combinar métodos abstratos e métodos concretos. O método abstrato formatar continua sendo a operação que cada subclasse precisa fornecer. Já formatar_todos concentra uma regra comum: percorrer os textos e formatar cada um deles.

Assim, as subclasses definem como um texto é formatado, enquanto a base define como aplicar essa operação a uma coleção.

Fluxo da formatação em lote

Diagrama mostrando uma chamada para formatar_todos em uma instância concreta, seguida de chamadas para formatar em cada texto e uma lista final de textos formatados.

O método concreto herdado chama a operação específica da instância para cada elemento.

Codifique a operação compartilhada

Base abstrata com método concreto

Crie ou atualize um arquivo Python no seu computador com este exemplo completo.

python
from abc import ABC, abstractmethod
from collections.abc import Iterable


class FormatadorTexto(ABC):
    @abstractmethod
    def formatar(self, texto: str) -> str:
        """Formata um único texto."""

    def formatar_todos(self, textos: Iterable[str]) -> list[str]:
        return [self.formatar(texto) for texto in textos]


class FormatadorMarkdown(FormatadorTexto):
    def formatar(self, texto: str) -> str:
        return f"**{texto}**"


class FormatadorColchetes(FormatadorTexto):
    def formatar(self, texto: str) -> str:
        return f"[{texto}]"


textos = ["aviso", "resumo"]

markdown = FormatadorMarkdown()
colchetes = FormatadorColchetes()

print(markdown.formatar_todos(textos))
print(colchetes.formatar_todos(textos))

Exemplo

Saída esperada

['aviso', 'resumo']
['[aviso]', '[resumo]']

Dica

Onde ocorre o despacho?

Dentro de formatar_todos, a chamada self.formatar(texto) é resolvida conforme a instância. Em um FormatadorMarkdown, ela executa a versão Markdown; em um FormatadorColchetes, executa a versão com colchetes. A lógica do laço não é duplicada.

Rastreie uma chamada em lote

Ordem de execução

Ordene o fluxo produzido por markdown.formatar_todos(["aviso", "resumo"]).

  1. formatar_todos devolve a lista com todos os textos formatados.
  2. FormatadorMarkdown.formatar transforma cada texto com ** **.
  3. A chamada chega a formatar_todos, método concreto herdado de FormatadorTexto.
  4. formatar_todos percorre cada texto e chama self.formatar(texto).

O método concreto não completa o contrato

Subclasse incompleta

Considere a classe abaixo. Qual afirmação está correta?

<code>class FormatadorPendente(FormatadorTexto):
pass</code>

Explique a reutilização

Em uma frase ou duas, explique por que formatar_todos fica na classe base, enquanto formatar fica a cargo das subclasses.

Escreva pelo menos 40 caracteres (0/40).

Passo 5 de 7

Separar instanciação, assinaturas e comportamento

Diferencie o bloqueio de classes abstratas, a análise estática de assinaturas e os testes de comportamento.

Três evidências, três garantias

Não confunda as camadas

Uma ABC responde a uma pergunta específica em tempo de execução: a classe ainda tem operações abstratas pendentes? Se tiver, ela não pode ser instanciada.

Isso não verifica automaticamente se a assinatura usada pela subclasse é compatível com a assinatura declarada na base. Essa comparação é trabalho do mypy. E mesmo uma assinatura aprovada não prova que o resultado produzido atende à regra do domínio: para isso, escreva testes.

Mapa das garantias

Diagrama com três etapas independentes: ABC bloqueia instanciação incompleta; mypy compara assinaturas; testes verificam o resultado observado.

Cada mecanismo cobre uma falha diferente; nenhum substitui os demais.

Atenção

Herança não valida todos os detalhes

Uma subclasse pode ser considerada concreta apenas por possuir um método com o nome abstrato. O mecanismo de ABC não confere sozinho quantidade, tipos ou retorno dos parâmetros da sobrescrita.

Quando a classe instancia, mas a assinatura está errada

Uma sobrescrita incompatível

Salve como formatadores.py e execute os dois comandos mostrados depois do código.

python
from abc import ABC, abstractmethod


class Formatador(ABC):
    @abstractmethod
    def formatar(self, texto: str) -> str:
        ...


class FormataComLargura(Formatador):
    # O nome coincide com o método abstrato, então a classe é concreta.
    # Mas a assinatura exige um argumento que a base não exige.
    def formatar(self, texto: str, largura: int) -> str:
        return texto.center(largura)


def exibir(formatador: Formatador, texto: str) -> str:
    return formatador.formatar(texto)


quebrado = FormataComLargura()  # A instanciação é permitida.
print(exibir(quebrado, "abc"))  # Em execução: TypeError

# No terminal:
# python formatadores.py
# python -m mypy --strict formatadores.py

Leia os dois resultados

A criação de FormataComLargura() é permitida porque não há método abstrato pendente. Porém, exibir chama formatar com apenas texto; por isso a execução termina em TypeError ao faltar largura.

Já o mypy deve apontar a definição de formatar como uma assinatura incompatível com a superclasse. Para substituir a base com segurança, a subclasse precisa aceitar as chamadas previstas por ela e retornar um valor compatível — neste caso, str.

Um objeto parecido não entra no contrato nominal

Acrescente este trecho ao mesmo arquivo e rode novamente o mypy.

python
class ApenasParecido:
    def formatar(self, texto: str) -> str:
        return f"<{texto}>"


parecido = ApenasParecido()
print(exibir(parecido, "abc"))
# Em execução, a chamada funciona porque Python não aplica a anotação.
# O mypy reporta: argumento incompatível para "exibir".

Atenção

Anotação não é uma barreira em execução

O parâmetro formatador: Formatador orienta ferramentas estáticas; ele não executa uma validação nominal automática quando a função é chamada. Sem o mypy, o segundo exemplo pode rodar se o objeto tiver o método esperado.

Assinatura certa ainda pode produzir resultado errado

Teste a regra, não só o tipo

Agora considere uma implementação com a assinatura correta, mas com uma regra de negócio incorreta. O mypy aceita a substituição porque parâmetros e retorno são compatíveis. Um teste deve verificar o resultado que o contrato documenta.

Teste de comportamento com pytest

Salve como test_formatadores.py, no mesmo diretório de formatadores.py, e execute python -m pytest.

python
from formatadores import Formatador


class FormataMaiusculasComDefeito(Formatador):
    def formatar(self, texto: str) -> str:
        return texto.lower()  # Assinatura válida, comportamento errado.


def test_formata_em_maiusculas() -> None:
    formatador = FormataMaiusculasComDefeito()

    assert formatador.formatar("Olá") == "OLÁ"

Diagnóstico em três camadas

Por que FormataComLargura pode ser instanciada, mas ainda deve ser corrigida? E por que o teste de maiúsculas continua necessário após o mypy aceitar uma implementação?

Escreva pelo menos 80 caracteres (0/80).

Escolha a evidência adequada

Associe o problema à verificação principal

Relacione cada situação ao mecanismo que revela diretamente o problema.

Toque em um item e depois no par correspondente.

Passo 6 de 7

Escolher entre ABC e Protocol

Compare contratos nominais e estruturais para decidir quando a herança explícita é requisito e quando basta oferecer operações compatíveis.

A relação que o contrato exige

Duas formas de compatibilidade

Use ABC quando fazer parte de uma hierarquia explícita é um requisito do domínio ou da arquitetura. A implementação declara essa participação ao herdar da base.

Use Protocol quando o consumidor precisa apenas de determinadas operações e deve aceitar classes independentes que já tenham assinaturas compatíveis. Nesse caso, não é necessário alterar a classe para fazê-la herdar do contrato.

Nominal e estrutural lado a lado

A diferença central é a relação exigida entre a implementação e o contrato.

Diagrama comparando uma família de subclasses ligada por herança a uma base abstrata e classes independentes ligadas apenas por métodos compatíveis a um protocolo.

ABC exige vínculo de herança; Protocol aceita compatibilidade pela estrutura.

Exemplo

Critério de decisão

Uma biblioteca define os formatadores oficiais do sistema e quer que toda implementação pertença claramente à família Formatador. ABC expressa esse requisito.

Já uma função que apenas precisa chamar formatar(texto: str) -> str pode receber um adaptador criado por outro pacote, sem modificar sua classe. Protocol evita impor uma herança que não é necessária.

Não confunda os benefícios

Garantias diferentes, complementares

Uma ABC pode reunir métodos abstratos e lógica concreta compartilhada, como formatar_lote. Isso é um benefício útil, mas não é motivo suficiente por si só para escolher ABC: uma composição ou uma função auxiliar também pode compartilhar lógica.

A escolha principal é: a implementação deve declarar que pertence a esta família? Se sim, ABC. Se não, e bastam as operações compatíveis, Protocol.

O que cada mecanismo verifica

Ambos podem participar da checagem estática de assinaturas; a obrigação de concretizar métodos antes de instanciar é específica da ABC.

Diagrama com três camadas: verificação estática de assinaturas para ABC e Protocol, bloqueio de instanciação para ABC com método abstrato pendente, e testes de comportamento para ambos.

Assinaturas, instanciação e comportamento são garantias distintas.

Dica

Formulação precisa

Evite justificar ABC dizendo que ela valida automaticamente a assinatura da sobrescrita ou o resultado do método. abstractmethod exige uma concretização para permitir instanciar; um verificador como mypy examina compatibilidade de assinaturas; testes verificam o comportamento esperado.

Decida pelo requisito

Contrato para integração externa

Um relatório recebe objetos de plugins de terceiros. Cada plugin já possui formatar(texto: str) -> str, mas seus autores não podem nem precisam herdar de uma classe do seu projeto. Qual contrato é mais adequado para anotar o consumidor?

Justifique as duas escolhas

ABC ou Protocol?

Considere dois requisitos:

  1. Os formatadores oficiais devem declarar participação na família Formatador e podem aproveitar uma operação concreta comum da base.
  2. Adaptadores de bibliotecas externas devem ser aceitos se fornecerem formatar(texto: str) -> str, sem que suas classes precisem ser alteradas.

Escolha ABC ou Protocol para cada requisito e justifique sem atribuir a eles garantias que pertencem ao mypy ou aos testes.

Escreva pelo menos 120 caracteres (0/120).

Passo 7 de 7

Aplicação final: completar e verificar o contrato

Complete uma nova implementação de formatador e reúna evidências distintas para concretização, compatibilidade estática e comportamento.

O desafio e as três evidências

Complete sem alterar o consumidor

Você vai adicionar TitleFormatter à família nominal de TextFormatter. A transformação exigida é: remover espaços nas extremidades e aplicar capitalização de título, como em " python AVANÇADO " → "Python Avançado".

Não altere TextFormatter, UpperFormatter nem a função renderizar. Ao final, a nova classe deve participar da mesma hierarquia e funcionar tanto para um texto quanto para uma lista de textos.

O contrato em ação

A função consumidora conhece apenas a base TextFormatter; cada instância concreta fornece sua própria implementação de format.

Diagrama mostrando TextFormatter como classe base abstrata, UpperFormatter e TitleFormatter como subclasses, e uma função renderizar recebendo a base e chamando format_all.

A extensão ocorre na família de subclasses, sem mudar quem consome o contrato.

Dica

Evidências complementares

Use três verificações, cada uma com um alcance diferente: a tentativa de instanciar uma classe incompleta confirma o bloqueio de abstração; mypy --strict examina as anotações e sobrescritas; pytest confirma os resultados esperados. Nenhuma delas, isoladamente, substitui as outras.

Implemente o novo formatador

Crie formatters.py

Copie este arquivo para o seu computador. Em TitleFormatter.format, substitua o corpo com raise NotImplementedError pela implementação solicitada, mantendo os tipos e a assinatura.

python
from abc import ABC, abstractmethod


class TextFormatter(ABC):
    @abstractmethod
    def format(self, text: str) -> str:
        """Formata um texto individual."""

    def format_all(self, texts: list[str]) -> list[str]:
        return [self.format(text) for text in texts]


class UpperFormatter(TextFormatter):
    def format(self, text: str) -> str:
        return text.upper()


class TitleFormatter(TextFormatter):
    def format(self, text: str) -> str:
        raise NotImplementedError  # Substitua por sua implementação.


def renderizar(formatter: TextFormatter, texts: list[str]) -> list[str]:
    return formatter.format_all(texts)

Exemplo

Implementação esperada

O corpo final de TitleFormatter.format deve devolver text.strip().title(). Essa implementação concreta satisfaz a operação obrigatória e torna TitleFormatter instanciável.

Verifique bloqueio, tipos e comportamento

Crie test_formatters.py

Este teste cria uma subclasse deliberadamente incompleta para demonstrar o TypeError, sem desfazer sua implementação de TitleFormatter.

python
import pytest

from formatters import TextFormatter, TitleFormatter, UpperFormatter, renderizar


class IncompleteFormatter(TextFormatter):
    pass


def test_subclasse_incompleta_nao_pode_ser_instanciada() -> None:
    with pytest.raises(TypeError):
        IncompleteFormatter()


def test_title_formatter_formata_um_texto() -> None:
    formatter = TitleFormatter()
    assert formatter.format("  python AVANÇADO  ") == "Python Avançado"


def test_renderizar_aceita_subclasses_da_mesma_familia() -> None:
    textos = ["olá mundo", "python"]

    assert renderizar(UpperFormatter(), textos) == ["OLÁ MUNDO", "PYTHON"]
    assert renderizar(TitleFormatter(), textos) == ["Olá Mundo", "Python"]

Execute as verificações

No terminal, na pasta que contém os dois arquivos, execute estes comandos depois de completar o método:

bash
python -m mypy --strict formatters.py test_formatters.py
python -m pytest -q

Atenção

O que cada resultado significa

Se TitleFormatter ainda apenas lançar NotImplementedError, ela pode continuar instanciável: para o mecanismo de ABC, o método deixou de ser abstrato. Os testes revelam que o comportamento ainda está errado. Do mesmo modo, uma aprovação do mypy não prova que a transformação produz o texto correto.

Registre sua conclusão

Evidências e escolha do contrato

No seu computador, complete o método e execute os comandos. Em seguida, registre: (1) o que ocorreu ao instanciar IncompleteFormatter; (2) o resultado do mypy e dos testes; e (3) por que ABC é apropriada neste caso em vez de um Protocol.

Escreva pelo menos 180 caracteres (0/180).

Resumo

Síntese final

  • ABC com @abstractmethod impede a instanciação enquanto houver operações abstratas pendentes.
  • Uma subclasse concreta pode reutilizar format_all da base e ser consumida por uma função anotada com TextFormatter.
  • A adesão nominal vem da herança explícita; possuir um método com o mesmo nome não basta para pertencer à família.
  • ABC verifica concretização em tempo de execução, mypy examina compatibilidade estática e testes verificam o comportamento observado.
  • Escolha ABC quando a participação explícita na hierarquia for requisito; escolha Protocol quando a compatibilidade independente de herança for desejada.

Contrato nominal concluído

Parabéns! Você concluiu: Definir contratos nominais com classes abstratas

Você concluiu a implementação e distinguiu as garantias de abstração, tipagem estática e testes de comportamento.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso