用functools.singledispatch优雅实现Python方法重载
【摘要】 不靠if-elif链,用标准库singledispatch按类型动态分发,实现可扩展的方法重载。
Python没有C++那样的函数重载,但标准库functools.singledispatch提供了基于第一个参数类型的动态分发,配合注册机制可以优雅地实现类似重载的效果。
一、为什么需要 singledispatch
传统做法是用if-elif-isinstance链判断类型,分支多了可读性差且违反开闭原则。singledispatch把「类型→处理函数」的映射交给装饰器管理,新增类型只需register,不必改原函数。
二、基础用法
from functools import singledispatch
@singledispatch
def serialize(obj):
raise TypeError(f'unsupported: {type(obj)}')
@serialize.register(str)
def _(obj):
return f'"{obj}"'
@serialize.register(int)
def _(obj):
return str(obj)
@serialize.register(list)
def _(obj):
return '[' + ','.join(serialize(x) for x in obj) + ']'
调用serialize时,根据第一个参数的实际类型路由到对应实现,未注册的类型走默认实现抛错。
三、register 的两种写法
装饰器形式@fun.register(int)最清晰;也可调用形式fun.register(int, impl)在无法用装饰器的场景动态注册。register返回原函数,可链式注册。
四、基于 ABC 的分发
singledispatch按类型mro匹配,所以注册抽象基类(如collections.abc.Sequence)可覆盖所有序列子类。注意:注册顺序不影响,按mro最近匹配优先。
from collections.abc import Sequence
@serialize.register(Sequence)
def _(obj):
return '[' + ','.join(serialize(x) for x in obj) + ']'
五、方法分发的坑
类方法上用singledispatch要小心self:singledispatchmethod(3.8+)专门解决此问题,它把self排除在分发参数外。普通singledispatch套在方法上会按self类型分发,通常不是想要的。
from functools import singledispatchmethod
class Renderer:
@singledispatchmethod
def render(self, obj):
raise TypeError
@render.register(str)
def _(self, obj):
return obj
六、小结
singledispatch适合「同一逻辑、多种类型」场景,比visitor模式更轻量。局限是只按第一个参数分发,多参数分发需自己组合。性能上是一次dict查找,比if-elif链在大分支数下更快。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)