Python: Sobrecarga de funções

Um recurso disponível há um bom tempo no python e aparentemente pouco conhecido, a sobrecarga de funções é muito útil para adicionar polimorfismo em nível de funções.

Existem hoje, pelo menos 2 modos de fazer isso nativamente e um terceiro modo utilizando uma biblioteca. Aqui só pretendo abordar apenas um: singledispatch.

Utilizando singledispatch

Singledispatch é um decorator que quando aplicado a uma função promete transformá-la em uma função genérica, ou seja, uma função composta por outras funções que podem receber argumentos de diferentes tipos. Assim, ao invés de você lotar sua aplicação com if…else, isinstance ou qualquer coisa do tipo, voce pode criar funções na forma de handlers simples para os tipos que você espera passar para sua função decorada com @singledispatch.

Após receberem o decorator @singledispatch, uma função passa a possuir um atributo registry e um método register.

Exemplo:

# test_singledispatched_func.py
from freezegun import freeze_time
from singledispatched_func import since

@freeze_time("2022-04-15")
def test_since_with_days():
    assert since(1)\
        .strftime("%d/%m/%Y") == "14/04/2022"


@freeze_time("2022-04-15")
def test_since_with_string_date():
    assert since("14/04/2022")\
        .strftime("%d/%m/%Y") == "14/04/2022"

# singledispatched_func.py
import datetime
from functools import singledispatch


@singledispatch
def since(inputdata):
    raise NotImplementedError("U Can't touch this!")

@since.register
def _(days: int):
    return (
        datetime.datetime.now() - 
        datetime.timedelta(days=days)
    )

@since.register  # type: ignore
def _(date_str: str):
    day, month, year = [
        int(item) for item in date_str.split('/')
    ]
    return datetime(
        year=year, 
        month=month, 
        day=day
)

Neste exemplo, uma função simples ‘since‘ que pode receber como argumento:
1 – um número (days) de dias e retornar um objeto datetime correspondente
2 – uma string de data em formato “DD/MM/YYYY” (pt-br) e retornar um objeto datetime correspondente

Só por curiosidade, ao verificarmos o conteúdo do atributo .registry, obtemos o seguinte:

In [55]: since.registry
Out[55]: 
mappingproxy({object: <function __main__.since(inputdata)>,
              int: <function __main__._(days: int)>,
              str: <function __main__._(date_str: str)>})

Esse é um jeito simples de organizar o código, reduzir ramificações lógicas e respeitar princípios da programação orientada a objetos. Vamos usar mais.

Dica: Para métodos, dê uma olhada em singledispatchmethod

As outras formas de fazer algo parecido são utilizando o typing.overload e a biblioteca dry-python/classes

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: