# Examples


# multimethod

Multimethods are a mapping of signatures (tuple of types) to functions. They maintain an efficient dispatch tree, and cache the called signatures.


``` python
import operator

from multimethod import multimethod

classic_div = multimethod(operator.truediv)
classic_div[int, int] = operator.floordiv
classic_div
```


    {(): <function _operator.truediv(a, b, /)>,
     (int, int): <function _operator.floordiv(a, b, /)>}


``` python
classic_div(3, 2)
```


    1


``` python
classic_div(3.0, 2)
```


    1.5


``` python
classic_div
```


    {(): <function _operator.truediv(a, b, /)>,
     (int, int): <function _operator.floordiv(a, b, /)>,
     (float, int): <function _operator.truediv(a, b, /)>}


Multimethods introspect type annotations and use the name to find existing multimethods.


``` python
import itertools
from collections.abc import Iterable, Sequence


@multimethod
def batched(values: Iterable, size):
    it = iter(values)
    return iter(lambda: list(itertools.islice(it, size)), [])


@multimethod
def batched(values: Sequence, size):
    for index in range(0, len(values), size):
        yield values[index : index + size]


list(batched(iter("abcde"), 3))
```


    [['a', 'b', 'c'], ['d', 'e']]


``` python
list(batched("abcde", 3))
```


    ['abc', 'de']


Multimethods also have an explicit [register](../reference/multimethod.md#multimethod.multimethod.register) method similar to `functools.singledispatch`.


``` python
@multimethod
def window(values, size=2):
    its = itertools.tee(values, size)
    return zip(*(itertools.islice(it, index, None) for index, it in enumerate(its)))


@window.register
def _(values: Sequence, size=2):
    for index in range(len(values) - size + 1):
        yield values[index : index + size]


list(window(iter("abcde")))
```


    [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')]


``` python
list(window("abcde"))
```


    ['ab', 'bc', 'cd', 'de']


# parametric

In addition to `issubclass`, multimethods can dispatch on `isinstance` with parametric checks.


``` python
import asyncio
import inspect
import time
from collections.abc import Callable
from concurrent import futures

from multimethod import parametric

Coroutine = parametric(Callable, inspect.iscoroutinefunction)


@multimethod
def wait(timeout, func, *args):
    return futures.ThreadPoolExecutor().submit(func, *args).result(timeout)


@multimethod
async def wait(timeout, func: Coroutine, *args):
    return await asyncio.wait_for(func(*args), timeout)


wait(0.5, time.sleep, 0.01)
```


``` python
wait(0.5, asyncio.sleep, 0.01)
```


    <coroutine object wait at 0x7fd58cd87920>


``` python
from array import array

IntArray = parametric(array, typecode="i")
isinstance(array("i"), IntArray)
```


    True


``` python
isinstance(array("f"), IntArray)
```


    False


# typing subscripts

Support for type hints with subscripts.


``` python
import bisect
import random


@multimethod
def samples(weights: dict):
    """Generate weighted random samples using bisection."""
    keys = list(weights)
    totals = list(itertools.accumulate(weights.values()))
    values = [total / totals[-1] for total in totals]
    while True:
        yield keys[bisect.bisect_right(values, random.random())]


@multimethod
def samples(weights: dict[object, int]):
    """Generate weighted random samples more efficiently."""
    keys = list(itertools.chain.from_iterable([key] * weights[key] for key in weights))
    while True:
        yield random.choice(keys)


weights = {"a": 1, "b": 2, "c": 3}
next(samples(weights))
```


    'c'


``` python
weights = {"a": 1.0, "b": 2.0, "c": 3.0}
next(samples(weights))
```


    'a'
