前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于 inspect 实现重载

基于 inspect 实现重载

作者头像
kifuan
发布2023-02-23 19:51:09
1860
发布2023-02-23 19:51:09
举报
文章被收录于专栏:随便写写-kifuan随便写写-kifuan

前言

通常来说,Python 中的重载依赖 typing.overload,如下:

代码语言:javascript
复制
from typing import overload, Any


@overload
def foo(a: int) -> None: ...


@overload
def foo(a: float) -> None: ...


def foo(a: Any) -> None:
    if isinstance(a, int):
        return

    if isinstance(a, float):
        return

    raise TypeError('...')

本文要实现的是借助 inspect 实现一个运行时帮助判断类型并调用指定函数的工具。

实现

为了简化逻辑,我们这里只支持普通的参数,也就是不支持 positional onlykeyword only 参数。

代码语言:javascript
复制
import inspect
from inspect import Parameter
from typing import Any, TypeVar, Callable, ParamSpec, Iterator


T = TypeVar('T')
P = ParamSpec('P')


def get_params(f: Callable[P, T]) -> Iterator[Parameter]:
    yield from inspect.signature(f).parameters.values()


def match(f: Callable[P, T], args: tuple[Any, ...]) -> bool:
    params = list(get_params(f))

    if len(params) != len(args):
        return False

    return all(
        isinstance(args[index], param.annotation)
        for index, param in enumerate(params)
    )


class Overload:
    def __init__(self) -> None:
        self.callables: set[Callable[..., T]] = set()

    def __call__(self, *args: Any) -> Any:
        for f in self.callables:
            if match(f, args):
                return f(*args)
        raise TypeError(f'unmatched arguments {args}')

    def register(self, f: Callable[P, T]) -> Callable[P, T]:
        for p in get_params(f):
            if p.kind != Parameter.POSITIONAL_OR_KEYWORD:
                raise TypeError('only allow normal arguments')
            if not isinstance(p.annotation, type):
                raise TypeError('annotations must be real types')
        self.callables.add(f)  # type: ignore
        return f

使用:

代码语言:javascript
复制
FIRST = 1
SECOND = 2
THIRD = 3


foo = Overload()


@foo.register
def _(a: int) -> int:
    print(f'calling foo with an integer: {a}')
    return FIRST


@foo.register
def _(a: float) -> int:
    print(f'calling foo with a float: {a}')
    return SECOND


@foo.register
def _(a: int, b: int) -> int:
    print(f'calling foo with two integers: {a}, {b}')
    return THIRD


assert foo(1) == FIRST
assert foo(3.14) == SECOND
assert foo(1, 2) == THIRD

就这样。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档