Python之单分派泛函数(singledispatch)

singledispatch 是标准库 functools 模块的函数 文档

单分派泛函数

根据第一个参数的类型,以不同方式执行相同操作的一组函数。在python里,使用 @singledispatch 装饰的普通函数会变成泛函数(generic function)。 当然,如果根据多个参数选择专门的函数,那就是多分派了。这里不介绍。

这是官方的demo:

  • 要定义一个泛型函数,应使用 @singledispatch装饰器进行装饰。 请注意分派是作用于第一个参数的类型,要相应地创建你的函数:

# -*- coding: utf-8 -*-
from functools import singledispatch


@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just sqy, ", end=" ")
    print(arg)
  • 要将重载的实现添加到函数中,请使用泛型函数的 register()属性。 它是一个装饰器。 对于带有类型标注的函数,该装饰器将自动推断第一个参数的类型:

@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 k, v in enumerate(arg):
        print(k, v)
  • 对于不使用类型标注的代码,可以将适当的类型参数显式地传给装饰器本身:

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


@fun.register(list)
def _(arg, verbose=False):
    if verbose:
        print("Enumerate this:")
    for k, v in enumerate(arg):
        print(k, v)
  • 在调用时,泛型函数会根据第一个参数的类型进行分派:

if __name__ == '__main__':
    fun(1, verbose=True)
    fun([1, 2, 4], verbose=True)
    fun(1.01, verbose=True)

代码执行结果:

Strength in numbers, eh? 1
Enumerate this:
0 1
1 2
2 4
Let me just sqy,  1.01

从结果上可以看到,当有定义其类型的函数时,会执行对应的方法_,否则直接执行fun方法。

注意事项:

  1. @singledispatch 不是为了把 Java 的那种方法重载带入Python。在一个类中为同一个方法定义多个重载变体,比在一个函数中使用一长串 if/elif/elif/elif 块要更好。但是这两种方案都有缺陷,因为它们让代码单元(类或函数)承担的职责太多。@singledispath 的优点是支持模块化扩展:各个模块可以为它支持的各个类型注册一个专门函数。

  2. 函数的名称无关紧要;_ 是个不错的选择,简单明了。

  3. 可以叠放多个 register 装饰器,让同一个函数支持不同类型。

应用:

在平时的业务开发中,可以应用在对不确定类型变量时,需要执行不同的业务流程, 这只是其中一种场景。

参考书籍: Fluent Python