Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3.5 - method overloading with @overload

There is an overloading package for Python 3.5+. Whith this package, it's possible to redefined methods, but with distinct type hints and its decorator will find out, which overloaded method should be called.

Common coding pattern:

class foo:
  def func(param):
    if instance(param, int):
      pass
    elif instance(param, str):
      pass
    elif instance(param, list):
      pass
    else:
      raise ValueError()

With @overload:

class foo:
  @overload
  def func(param: int):
    pass

  @overload
  def func(param: str):
    pass

  @overload
  def func(param: list):
    pass

Here is the documentation.


My questions are:

  • How big is the performance impact compared to old style parameter type switching?
  • And how does this package access the type hints?
like image 485
Paebbels Avatar asked Mar 11 '23 00:03

Paebbels


2 Answers

You'd have to measure it on your own with a real code.

I made a very quick look at the code of this library and conclusion is simple. It uses a lot of reflection (inspect package) and type comparison. inspect package on its own is mostly used by debugging tools - they always slow your code down.

Just look at these lines:

complexity = complexity_mapping[id]
if complexity & 8 and isinstance(arg, tuple):
     element_type = tuple(type(el) for el in arg)
elif complexity & 4 and hasattr(arg, 'keys'):
     element_type = (type(element), type(arg[element]))
else:
     element_type = type(element)

type_hints = typing.get_type_hints(func) if typing else func.__annotations__
types = tuple(normalize_type(type_hints.get(param, AnyType)) for param in parameters)

Note that this package if over 7 months old and has only 70 stars. Python is not Java... You'd really hurt python itself with this package :D You'd better implement some core api method that delegates calls to other methods/objects, based on type parameters - just like it should be done with Python.

like image 35
adgon92 Avatar answered Mar 20 '23 20:03

adgon92


Since python 3.4 there is a core API functionality functools.singledispatch, which allows you to register overloading functions.

From the documentation

>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)

>>> @fun.register
... def _(arg: int, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)

>>> @fun.register
... def _(arg: list, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

When running the functions above (again from the documentation):

>>> fun("Hello, world.")
Hello, world.
>>> fun("test.", verbose=True)
Let me just say, test.
>>> fun(42, verbose=True)
Strength in numbers, eh? 42
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam

CAUTION: Only the first argument is typed!

Furthermore, there is (since python 3.8) also a equivalent decorator for class methods call functools.singledispatchmethod

like image 157
GrazingScientist Avatar answered Mar 20 '23 20:03

GrazingScientist