Callable Python
Tags: python
Published: 2020-05-09 19:42:55

## Introdução

Python tem diversos métodos mágicos que, às vezes, podem ser realmente confusos.
Muitas vezes não percebemos a real necessidade de um determinado método, ou não
sabemos como aplicá-lo no mundo real.

Nesse post, explicarei uma utilização real de um deles, o famoso `__call__`.


## Classe Abstrata

A linguagem Python tem suporte à classe abstrata
[Callabe](https://docs.python.org/pt-br/3/library/collections.abc.html#collections.abc.Callable),
que nos auxilia na criação de uma classe compatível com o protocolo Callable,
criando uma classe que herde de Callable nos auxilia na correta implementação da
mesma.

Para utilizar esse classe abstrata basta importá-la da biblioteca abc: `from
collections.abc import Callabe` e herdá-la na sua própria classe.


## Utilização

Nesse post, irei exemplificar com a utilização de um Mapper direto de um ORM (no
caso, [SQLAlchemy]()), para um objeto, ou classe, de domínio.
Ou seja, iremos transformar os campos do ORM, para um objeto Python sem nenhum
acoplamento ou dependências externas.

Classe Callable:

``` python
from .dog_domain import Dog
from collections.abc import Callable


class DogMapper(Callable):
    @staticmethod
    def to_domain(dog):
        return Dog(name=dog.name, legs=dog.legs, color=dog.color,) if dog else None

    def __call__(self, dog):
        return self.to_domain(dog)
```

Classe ORM de exemplo:

``` python
from app import db
from .dog_mapper import DogMapper


class Dog(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    legs = db.Column(db.Integer)
    color = db.Column(db.Integer)
    
    @property
    def mapper(self):
        return DogMapper()(self)
```

Classe de domínio:

``` python
class Dog:
    def __init__(self, name, legs, color):
        self.name = name
        self.legs = legs
        self.color = color

    @clasmethod
    def create(cls, name, color, legs=4):
        return cls(name, color, legs,)

```


## Explicação

Nesse caso, utilizamos a property **mapper** para realizar a inicialização do
mapper, e a conversão para o objeto de domínio automaticamente. O que facilita
bastante caso tivéssemos que obter alguma classe por id, depois importar o
Mapper e depois realizar a conversão.

Feito dessa forma, o **callable** é utilizado somente sob demanda, e quando
necessário. Assim não realizamos a conversão sem que haja real necessidade.

Assim, podemos utilizar o mapper diretamente do model, logo na sequência, 
como por exemplo:

``` python
dog = Dog.query.get(id=15).mapper
```

## Conclusão

Nesse post tentei demonstrar uma utilização real de uma classe Callable, espero
que tenha ajudado! 
Caso tenha alguma dúvida, ou sugestão, não hesite em comentar. :wink:

Nos vemos por aí!

## Referências

- [Python Fluente](https://www.amazon.com/Python-Fluente-Em-Portuguese-Brasil/dp/857522462X) 
- [Clean Architecture](https://www.amazon.com/Arquitetura-Artesao-Estrutura-Software-Portugues/dp/8550804606/ref=sr_1_1?dchild=1&keywords=arquitetura+limpa&qid=1588281300&s=books&sr=1-1) 
- [Domain Driven Design](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software-ebook/dp/B00794TAUG/ref=sr_1_1?crid=38ZVRV16CDUMU&dchild=1&keywords=domain+driven+design&qid=1588281345&s=books&sprefix=domain+driven%2Cstripbooks-intl-ship%2C358&sr=1-1) 

Mais posts como esse, olhe no [meu blog](https://vinimelo.dev)