Classes Protocol, a “Interface” do Python — Revendo o artigo sobre IoC

Cassio R. Eskelsen
3 min readNov 7, 2021

--

No artigo sobre IoC mencionei que Python não tinha o conceito de Interface e por isso utilizaríamos classes Abstratas como opção.
Bom, confesso que não sou um bom exemplo como leitor de release notes e com isso acabo deixando algumas novidades de lado. Isso aconteceu com as Classes Protocol introduzidas na versão 3.8 do Python.

Em minha defesa, aparentemente é uma novidade ainda não muito adotada pela comunidade. Talvez pelo fato de ser considerado mais pythonico utilizar duck typing ao invés de recursos comuns em outras linguagens OO.

Classes Protocol são especificadas pela PEP 0544 como uma forma de structural typing. Ou seja, uma classe é considerada compatível com a Classe Protocol se existir uma correspondência de elementos entre as duas classes.

Não há necessidade de a implementação concreta especificar a Classe Protocol que implementa. Por exemplo, o código abaixo é totalmente válido:

Veja que na linha 23 não especificamos que MongoCustomerRepository implementa a classe Protocol CustomerRepository.

Muito provavelmente o primeiro pensamento que você terá é de que isso é perda de tempo, não é necessário especificar o tipo na função ExemploService pois o duck typing vai garantir o funcionamento.
Não tenho como discordar deste pensamento, no entanto você irá perder um grande benefício: type hinting. Imagine você trabalhando em um grande projeto e precisa sempre descobrir qual é o tipo do parâmetro que a função/método aceita. Com type hinting a IDE irá mostrar para você que tipo de valor é esperado.

Perceba que não precisamos implementar todos os métodos abstratos em MongoCustomerRepository. Isso poderá nos trazer alguns problemas pois se estamos usando Protocol como uma Interface, acreditaremos que todos os métodos estarão implementados. Além disso, precisamos ter em mente o item 2 do Zen of Python: “Explicit is better than implicit”.
Vamos então refatorar a classe MongoCustomerRepository para endereçar essas considerações, explicitando qual Classe Protocol MongoCustomerRepository implementa:

Uma operação comum é verificar se uma instância é do tipo x. Em Python faríamos algo como:

isinstance(mc, CustomerRepository)

No entanto, no caso das classes Protocol isso não funciona por padrão. Se tentarmos isso, será disparada uma exception com a seguinte mensagem: “TypeError: Instance and class checks can only be used with @runtime_checkable protocols”.

A mensagem do erro já está explicitando a solução: precisamos marcar a Classe Protocol com o decorator @runtime_checkable:

O que muda no Artigo sobre IoC?

A única mudança será no arquivo domain/customer/customer_repository.py . Substituiremos ABC (que indica que é uma classe abstrata) por Protocol, e adicionaremos o decorator @runtime_checkable:

Porque considero esse tipo de adição importante no Python

Como comentei acima, o Python já possuía mecanismos que permitiam fazer coisas semelhantes e alguns mais radicais acreditam que essas formas antigas são mais condizentes com o estilo Python de ser.

No entanto, temos que levar em conta que o Python cresceu e está crescendo muito no mercado. Para conseguir crescer mais precisa ganhar espaço em aplicações “enterprise”. Nesse tipo de aplicação um nível mínimo de formalismo é necessário, pois precisamos da produtividade das IDEs, temos que ter mecanismos que nos ajudem a garantir a qualidade do código e necessitamos diminuir o preconceito de quem vem de linguagens mais clássicas no mundo corporativo como o C#, Java, etc e enxerga o Python como uma linguagem um tanto “libertina”.

Nada indica que em no médio (e até longo) prazo o Python deixe de ser dinâmico e vire estático como as outras linguagens. Mas isso não impede que nós desenvolvedores Python subamos a régua em termos de design e arquitetura de nossos sistemas. O custo para isso não é alto e o ganho é enorme.

--

--