functools.singledispatchmethod(Python 3.8)

栏目: Python · 发布时间: 5年前

内容简介:functools模块里面的函数是非常常用和有用的,凡是这个模块新增的内容都是值得了解的。这篇文章将介绍Python 3.8新增的singledispatchmethod。singledispatch在我的书里面也提过。Python 3.4时为functools模块引入了将普通函数转换为泛型函数的工具singledispatch。先铺垫点知识:我们通过json序列化的例子理解一下,下面这个报错相信很多同学见过:

前言

functools模块里面的函数是非常常用和有用的,凡是这个模块新增的内容都是值得了解的。这篇文章将介绍 Python 3.8新增的singledispatchmethod。

复习singledispatch

singledispatch在我的书里面也提过。Python 3.4时为functools模块引入了将普通函数转换为泛型函数的工具singledispatch。先铺垫点知识:

  1. 泛型函数:泛型函数是指由多个函数组成的函数,可以对不同类型实现相同的操作,调用时应该使用哪个实现由分派算法决定
  2. Single dispatch:一种泛型函数分派形式,基于单个参数的类型来决定

我们通过json序列化的例子理解一下,下面这个报错相信很多同学见过:

In : from datetime import datetime, date

In : now = datetime.now()

In : d = {'now': now, 'name': 'XiaoMing'}

In : import json

In : json.dumps(d)
...
/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py in default(self, o)
    177
    178         """
--> 179         raise TypeError(f'Object of type {o.__class__.__name__} '
    180                         f'is not JSON serializable')
    181

TypeError: Object of type datetime is not JSON serializable

datetime(date)类型不能直接json序列化。常见的解决方案是指定default参数:

In : def json_encoder(obj):
...:     if isinstance(obj, (date, datetime)):
...:         return obj.isoformat()
...:     raise TypeError(f'{repr(obj)} is not JSON serializable')
...:
In : json.dumps(d, default=json_encoder)
Out: '{"now": "2019-05-29T21:20:08.439376", "name": "XiaoMing"}'

方案:如果一个对象obj是datetime和date类型,序列化时值直接用 obj.isoformat() 转成了字符串。

如果用singledispatch可以这样写:

In : from functools import singledispatch

In : @singledispatch
...: def json_encoder(obj):
...:     raise TypeError(f'{repr(obj)} is not JSON serializable')
...:

In : @json_encoder.register(date)
...: @json_encoder.register(datetime)
...: def encode_date_time(obj):
...:     return obj.isoformat()
...:

In : json.dumps(d, default=json_encoder)
Out: '{"now": "2019-05-29T21:20:08.439376", "name": "XiaoMing"}'

可以看到,通过singledispatch装饰器把json_encoder函数转化成泛型函数。

singledispatch优势和应用场景?

我工作到现在基本没有在代码里面使用过singledispatch这种代码设计风格。就如上面的json序列化问题,我会选择写一个 json_encoder 函数,在里面用if/elif/else处理不同的类型问题:我会觉得这样逻辑更紧凑可读性好。

写这篇文章前我还特意搜了一些知名/主流项目、开发者、组织,绝绝大多数都没有用它。那把它放在标准库且是在一个很重要的模块里面的重要意义是什么呢?

延伸阅读链接2是关于singledispatch的PEP 443作者,也是Python核心开发、Python3.8 的发布经理(Release Manager)Łukasz Langa的相关博客文章,这里面的内容比较有价值。Łukasz介绍了为什么你应该使用singledispatch,看完之后我是这么总结的:

  1. 作者对这类代码设计风格的喜好。这个有点智者见智仁者见仁了,我觉得使用singledispatch之后一方面由于整体逻辑分散增加了代码行数,另外一方面我认为使用模式会不必要的增加代码阅读的难度。
  2. 更好的性能。这个需要阅读它的源码才可以理解,如果是常规的if/elif/else,每一次判断都要做一次一次类型检查,如果某次判断恰好符合else部分的,前面的那些if/elif都不能少,这是一种性能浪费;而由singledispatch包装之后,分发算法在模块被导入后就已经缓存起来了(只进行过这一次类型检查,存在 dispatch_cache 里),选择分发是一次Hash查找( key in dict ),很快。

讲道理,我们应该选性能最好的这个。但事实上业务里面用if/elif/else还是singledispatch的性能差别是很小的:绝大部分情况下小到用户感受不到,所以我个人的倾向是 不使用singledispatch

如果你有自己的理解,欢迎留言~

singledispatchmethod

singledispatch主要针对的是函数,但对于方法不友好,举个例子:

In : class Dispatch:
...:     @singledispatch
...:     def foo(self, a):
...:         return a
...:
...:     @foo.register(int)
...:     def _(self, a):
...:         return 'int'
...:
...:     @foo.register(str)
...:     def _(self, a):
...:         return 'str'
...:

In : cls = Dispatch()

In : cls.foo(1)
Out: 1  # 没有返回 'int'

In : cls.foo('s')
Out: 's'  # 没有返回 'str'

也就是 singledispatch 在方法上失效了。现在可以用singledispatchmethod来做了:

>>> from functools import singledispatchmethod
>>> class Dispatch:
...     @singledispatchmethod
...     def foo(self, a):
...         return a
...
...     @foo.register(int)
...     def _(self, a):
...         return 'int'
...
...     @foo.register(str)
...     def _(self, a):
...         return 'str'
...
>>> cls = Dispatch()
>>> cls.foo(1)
'int'
>>> cls.foo('s')
'str'

这种模式还可以用在classmethod、staticmethod、abstractmethod等装饰器上,如官网的例子:

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

以上就是Python3.8带来的singledispatchmethod的用途了。

延伸阅读


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微商思维

微商思维

龚文祥、罗剑锋、触电会 / 金城出版社 / 2018-7 / 88.00元

微商不仅仅是一种继传统实体、电商之后的革命性新兴商业形态,更是一种能够写入中国商业史的思潮。龚文祥新著《微商思维》,从道的层面对广大微商人的商业实践智慧进行了高度浓缩与抽象总结,站在更高的视角解读微商背后的商业逻辑与本质。 本书前半部分,主要从本质、品牌、营销等几个方面,阐述了微商思维的内涵及应用场景,帮助读者了解并认识这种革命性的商业思维。 后半部分主要是触电会社群内部各位大咖的实操......一起来看看 《微商思维》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具