跳转到主要内容

未提供项目描述

项目描述

Dyndis

pip install dyndis

关于

Dyndis是一个库,可以轻松地流畅地创建多分派函数和方法。它最初是为非严格分层系统中的运算符而制作的,但也可以用于任何其他多分派目的。

简单示例

from typing import Union

from dyndis import MultiDispatch


@MultiDispatch
def foo(a, b):
    # default implementation in case no candidates match
    raise TypeError


@foo.register()
def _(a: int, b: Union[int, str]):
    return "overload 1 <int, (int|str)>"


@foo.register()
def _(a: object, b: float):
    return "overload 2 <any, float>"


foo(1, "hello")  # overload 1
foo(("any", "object", "here"), 2.5)  # overload 2
foo(2, 3)  # overload 1
foo(2, 3.0)  # overload 2

功能

  • 动态升级。
  • 无缝使用类型提示和类型变量。
  • 高级数据结构以最小化候选查找时间。
  • 实现者接口使其易于创建类似于方法的重载

它是如何工作的?

中心类(用户只需导入一个)是MultiDispatchMultiDispatch包含按优先级和它们接受的参数类型排序的候选实现。当调用MultiDispatch时,它调用其相关候选者(按优先级、继承和兼容性排序,下面将详细说明),直到其中一个返回非NotImplemented的返回值。

查找顺序

所有类型参数的候选者(<T0, T1, T2..., TN>)按以下顺序排序

  • 任何与键中任何类型不兼容的候选者都被排除。也就是说,对于任何0 <= i <= N,如果候选者的参数i的类型约束不是Ti的超类,则该候选者被排除。
  • 候选者按继承排序。如果一个候选者的所有参数类型都是另一个的相应参数类型的子类(或同样受其覆盖),则认为该候选者继承自另一个候选者。一个候选者将在它继承的任何其他候选者之前被考虑。例如,<int,object>将先于<Number, object>被考虑。

如果有两个候选者不相互继承,则会抛出一个异常(类型为 dyndis.AmbiguityError),除非首先成功执行优先级更高的候选者。

如果候选者返回 NotImplemented,则尝试下一个候选者。如果没有候选者被接受或所有候选者都返回了 NotImplemented,则调用默认实现。

拓扑和缓存

dyndis 使用拓扑集对所有候选者按参数类型进行排序,这样就可以在不产生任何开销的情况下忽略大多数候选者。

对于每次查找都考虑所有这些候选者会非常慢,并且很快就会变得笨重。因此,每个 MultiDispatch 都会自动缓存这些计算,包括排序和处理候选者。

默认参数、可变参数和关键字参数

  • 如果候选者具有具有默认值和类型注解的位置参数,则默认值将被忽略以用于候选者解析。
  • 如果候选者具有可变位置参数,则会被忽略。当从 MultiDispatch 调用时,其值始终为 ()
  • 如果候选者具有仅关键字参数,则该参数不会用于候选者类型,它必须在调用 MultiDispatch 时具有默认值或设置。
  • 如果候选者具有可变关键字参数,则会被忽略。当从 MultiDispatch 调用时,其值将根据(类型忽略的)关键字参数来确定。

通常,当使用关键字参数调用 MultiDispatch 时,这些参数不会用于候选者解析,并且会原样发送给每个尝试的候选者。

实现者

Implementor 是一个描述符,它使得在类内部创建方法样式的候选者变得容易。

from dyndis import MultiDispatch


@MultiDispatch
def add(self, other):
    return NotImplemented


class Base:
    __add__ = add


class A(Base):
    @add.implement(__qualname__)
    def add(self, other: 'A'):
        # in implementor methods, `self` is assumed to be of the owner class
        return "A+A"

    @add.implement(__qualname__)
    def add(self, other: Base):
        return "A+Base"


class B(Base):
    @add.implement(__qualname__)
    def add(self, other: A):
        return 'B+A'


a = A()
b = B()
base = Base()
a + b  # A+B
a + base  # A+Base
a + a  # A+A

特殊类型注解

类型注解可以是任何类型,或者以下特殊值之一

  • typing.Union:接受包含类型中的任何类型的参数
  • typing.Optional:接受包含类型或 None
  • typing.Any:被认为是任何类型的超类型,包括 object
  • typing 的任何别名和抽象类,如 typing.Listtyping.Sized:等同于其原始类型(注意,专门化的别名如 typing.List[str] 是无效的)
  • typing.TypeVar:见下文
  • None...NotImplemented:等同于其类型

TypeVar 注解

参数还可以用 typing.TypeVar 注解。这些变量在遇到时会贪婪地绑定,并且在第一次绑定时就被视为匹配。第一次绑定后,它们被视为所有方面的绑定类型(或 TypeVar 的最低约束)。

from typing import TypeVar, Any

from dyndis import MultiDispatch

T = TypeVar('T')


@MultiDispatch
def foo(*args):
    raise TypeError


@foo.register()
def _(a: T, b: T):
    return "type(b) <= type(a)"


@foo.register()
def _(a: Any, b: Any):
    return "type(b) </= type(a"


foo(1, 1)  # <=
foo(1, True)  # <=
foo(2, 'a')  # </=
foo(object(), object())  # <=
# type variables bind greedily, meaning their exact value will be equal to the first type they encounter
foo(False, 2)  # </=

项目详情


下载文件

下载适合您平台的自定义文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源码分发

dyndis-0.2.0.tar.gz (11.2 kB 查看哈希值)

上传时间 源码

构建分发

dyndis-0.2.0-py3-none-any.whl (10.4 kB 查看哈希值)

上传时间 Python 3

由以下支持