python: methoddispatch

本文介绍了Python中的methoddispatch包和functools.singledispatchmethod,讲解了如何实现泛型函数,以及它们在类方法中的应用。包括注册、分派机制、类型查找和子类重写等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:

Python methoddispatch包_程序模块 - PyPI - Python中文网

functools.singledispatchmethod(Python 3.8) - 知乎

摘要:本文章介绍了methoddispatch包的基本使用,以及如果在类外注册泛型函数,泛型函数的分派机制,通过属性调用查看指定类型会被分派的执行函数,查看泛型函数的所有注册函数,子类重写扩展父类的泛型函数,在单个实例上进行指定类型函数的重写等;简单提及python3.8中的functools.singledispatchmethod

1. methoddispatch包简介

        python3.4向functools标准库中添加了singledispatch装饰器(主要针对函数),而methoddispatch 包则将此项功能添加到了实例方法中。

2. 什么是泛型函数?

        泛型函数是指由多个函数组成的函数,可以对不同类型实现相同的操作,调用时应该使用哪个实现由分派算法决定

3. methoddispatch包的使用

from methoddispatch import singledispatch, SingleDispatch
from decimal import Decimal


class MyClass(SingleDispatch):
    @singledispatch
    def func(self, arg, verbose=False):
        if verbose:
            print("Let me just say,", end=" ")
        print(arg)

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

    @func.register(list)
    def func_list(self, arg, verbose=False):
        if verbose:
            print("Enumerate this:")
        for i, elem in enumerate(arg):
            print(i, elem)

    @func.register(float)
    @func.register(Decimal)
    def func_num(self, arg, verbose=False):
        if verbose:
            print("Half of your number:", end=" ")
        print(arg / 2)

若要定义泛型方法的定义,使用@singledispatch修饰;分派发生在第一个参数的类型上,相应的创建函数。要向函数添加重载实现,使用泛型函数的register()属性;它是一个装饰器,获取一个类型参数并装饰一个实现该类型操作的函数。

register()属性仅在类语句中工作,依赖于SingleDispatch.__init_subclass__创建实际调度表。这也意味着无法注册具有相同名称的两个方法,因为只有最后一个将出现在类字典中。

未定义在类中的函数想注册到泛型函数可以使用 add_overload 属性

def nothing(obj, args, verbose=False):
    print("Nothing. ")

MyClass.func.add_overload(type(None), nothing)

当泛型函数被调用时,会根据传入的第一个参数的类型进行分派

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

如果特定类型没有注册的实现,则使用其方法解析顺序来查找更通用的实现。用@singledispatch修饰的原始函数注册为基object类型,这意味着如果找不到更好的实现,就使用它。

在检查泛型函数将为给定类型选择哪个实现,请使用dispatch()属性

>>> a.func.dispatch(flost)
<function MyClass.func_num at 0x000002C0E535F950>
>>> a.func.dispatch(dict)
<function MyClass.func at 0x000002C0E5355510>

要访问所有注册的实现,请使用只读的registry属性

>>> a.func.registry.keys()
dict_keys([<class 'object'>, <class 'int'>, <class 'list'>, <class 'decimal.Decimal'>, <class 'float'>, <class 'NoneType'>])
>>> a.func.registry[float]
<function MyClass.func_num at 0x000002236A7FF950>
>>> a.func.registry[object]
<function MyClass.func at 0x000002236A7F5510>

子类可以用它们自己的重写扩展基类上函数的类型注册表。SingleDispatch minix类确保每个子类都有自己独立的调度注册表副本

class SubClass(MyClass):
    @MyClass.func.register(str)
    def func_str(self, arg, verbose=False):
        print("Str.")


>>> a = MyClass()
>>> a.func("hello")   
hello
>>> b = SubClass()
>>> b.func("hello")
Str.

方法重写不需要再次提供 register 装饰器

class SubClass2(MyClass):
    def func_int(self, arg, verbose=False):
        print("Int")

>>> c = SubClass2()
>>> c.func_int(100)
Int

但是,为register decorator提供相同的类型也可以。用不同类型装饰一个方法重写(不是一个好主意)将被注册成不同的类型,并为原始类型保留基类处理程序。

如果需要,可以在单个实例上指定方法重写

def func_str(arg, verbose=False):
    print("String.")


>>> d = MyClass()
>>> d.func.register(str, func_str)

>>> d.func("Hello! ")
String.
>>> e = MyClass()
>>> e.func("Hello! ")
Hello! 

在Python3.6及更高版本中,对于用类型注释的函数,修饰符将自动推断第一个参数的类型

class MyClassAnno(SingleDispatch):
    @singledispatch
    def func(self, arg):
        print("Default.")

    @func.register
    def func_int(self, arg: int):
        print("Int.")


class SubClassAnno(MyClassAnno):
    @MyClassAnno.func.register
    def func_str(self, arg: str):
        print("Str.")

在Python3.5和更早版本中,SingleDispatch 类使用一个元类SingleDispatchMeta来管理调度注册表。但是在python3.6和更高版本中,使用了__init_subclass__方法。如果您的类也继承自ABC接口,那么您可以在python3.5及更高版本中使用SingleDispatchABCMeta元类。

最后,通过类访问方法func, 将使用该类的调度注册表

>>> SubClass2.func(s, 1)
Subclass int
>>> MyClass.func(s, 1)
1

4. 在python3.8中,可以通过使用functools.singledispatchmethod

class Negator:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    @classmethod
    def _(cls, arg: int):
        return -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool):
        return not arg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值