import types
from functools import update_wrapper
from inspect import signature, Parameter


class MultiMethod:

    def __init__(self, func):
        update_wrapper(self, func)
        self._method_dict = {}

        self.add_method(func)

    def add_method(self, func):
        sig = signature(func)
        # 此处 self 不需要 注解
        param_types = tuple(param.annotation for param in sig.parameters.values() if param.name != 'self')
        if Parameter.empty in param_types:
            raise TypeError("annotation required for all params")
        if param_types in self._method_dict:
            raise TypeError("duplicate method signature")
        self._method_dict[param_types] = func
        return self

    def __call__(self, *args):
        # args 第一个参数是 foo实例,用来手动传入,绑定func的 self
        # 后面的才是实际的参数
        param_types = tuple(type(arg) for arg in args[1:])
        if param_types in self._method_dict:
            return self._method_dict[param_types](*args)
        else:
            raise TypeError("not method found")

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            # foo.bar 时 手动绑定,返回一个绑定方法对象
            return types.MethodType(self, instance)


class Foo:
    @MultiMethod
    def bar(self, a: int):
        print(a)

    @bar.add_method
    def bar(self, a: int, b: int):
        print(a, b)


if __name__ == "__main__":
    f = Foo()
    print(f.bar)
    f.bar(1)
    f.bar(1, 2)
    f.bar('1')