2

Goal: create a single-dispatch generic function; as per functools documentation.

I want to use my_func() to calculate dtypes: int or list, in pairs.

Note: I've chosen to implement type hints and to raise errors for my own test cases, outside of the scope of this post.

For many argument data type hints; this post uses many @my_func.register(...) decorators.

Code:

from functools import singledispatch

from typeguard import typechecked
from typing import Union


@singledispatch
@typechecked
def my_func(x, y):
    raise NotImplementedError(f'{type(x)} and or {type(y)} are not supported.')


@my_func.register(int)
def my_func(x: int, y: int) -> Union[int, str]:
    try:
        return round(100 * x / (x + y))
    except (ZeroDivisionError, TypeError, AssertionError) as e:
        return f'{e}'


@my_func.register(list)
def my_func(x: list, y: list) -> Union[int, str]:
    try:
        return round(100 * sum(x) / (sum(x) + sum(y)))
    except (ZeroDivisionError, TypeError, AssertionError) as e:
        return f'{e}'


a = my_func(1, 2)
print(a)

b = my_func([0, 1], [2, 3])
print(b)
(venv) me@ubuntu-pcs:~/PycharmProjects/project$ python3 foo/bar.py 
/home/me/miniconda3/envs/venv/lib/python3.9/site-packages/typeguard/__init__.py:1016: UserWarning: no type annotations present -- not typechecking __main__.my_func
  warn('no type annotations present -- not typechecking {}'.format(function_name(func)))
Traceback (most recent call last):
  File "/home/me/PycharmProjects/project/foo/bar.py", line 22, in <module>
    @my_func.register(list)
AttributeError: 'function' object has no attribute 'register'
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
DanielBell99
  • 896
  • 5
  • 25
  • 57
  • 1
    It is expected for builtin package to be shown as not found by pip. – Bijay Regmi Dec 05 '22 at 09:19
  • 1
    Also, since you are reusing same name `my_func` for function in position 2 and 3, by the time the command is executed after 2nd `my_func` it overwrites `singledispatch` and `typechecked` function from 1st place and creates a `my_func` instance without these attributes. Hence it does not have `register` attribute. If you would rename 2nd and 3rd `my_func` to `my_func2` and `my_func3` respectively, you would not have the problem. – Bijay Regmi Dec 05 '22 at 09:28
  • I agree functions/ objects shouldn't have the same name; but I assumed that was the part of the point with `@singledispatch` – DanielBell99 Dec 05 '22 at 09:37

1 Answers1

1

Functions must be uniquely named

Credit to @Bijay Regmi for pointing this out.

@typechecked is placed above only the polymorphed @my_func.register functions; not above the @singledispatch function.

Note: you still invoke my_func(); I am just testing the polymorphed functions.

from functools import singledispatch

from typeguard import typechecked
from typing import Union


@singledispatch
def my_func(x, y):
    raise NotImplementedError(f'{type(x)} and or {type(y)} are not supported.')


@my_func.register(int)
@typechecked
def my_func_int(x: int, y: int) -> Union[int, str]:
    try:
        return round(100 * x / (x + y))
    except (ZeroDivisionError, TypeError, AssertionError) as e:
        return f'{e}'


@my_func.register(list)
@typechecked
def my_func_list(x: list, y: list) -> Union[int, str]:
    try:
        return round(100 * sum(x) / (sum(x) + sum(y)))
    except (ZeroDivisionError, TypeError, AssertionError) as e:
        return f'{e}'


a = my_func_int(1, 2)
print(a)

b = my_func_list([0, 1], [2, 3])
print(b)
(venv) me@ubuntu-pcs:~/PycharmProjects/project$ python3 foo/bar.py 
33
17
DanielBell99
  • 896
  • 5
  • 25
  • 57