Python优雅地dumps非标准类型

文章标题的参考文章: Python 优雅地 dumps 非标准类型

今天主要学习的是里面的知识点singledispatch

在Java语言中我们知道是有方法重载的(同一个方法名,参数不同或顺序不同导致),但是在Python中是没有方法重载这么一说的。不过在Python中我们可以使用singledispatch装饰器将一个普通函数变为泛函数(generic function)。

如果我们想针对不同类型的参数进行不同的处理,并且不想将他们放到同一个函数中,我们就可以使用singledispatch装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
In [1]: from functools import singledispatch

In [2]: @singledispatch
...: def typecheck():
...: pass
...:
In [6]: @typecheck.register(str)
...: def _(text):
...: print(type(text))
...: print("str--")
...:

In [7]: @typecheck.register(list)
...: def _(text):
...: print(type(text))
...: print("list--")
...:

In [8]: @typecheck.register(int)
...: def _(text):
...: print(type(text))
...: print("int--")
...:

In [9]: typecheck(1)
<class 'int'>
int--

In [11]: typecheck("1")
<class 'str'>
str--

In [12]: typecheck([1,2])
<class 'list'>
list--

singledispatch机制一个显著特征是,可以在系统的任何地方和任何模块中注册专门的函数。如果后来模块中增加了新的类型,可以直接添加一个新的专门用来处理新类型的函数。

参考文章: Python - lru_cache和singledispatch装饰器

有了上面的知识后我们再看下这篇文章的题目,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
In [13]: import json

In [14]: from datetime import datetime

In [15]: from decimal import Decimal

In [16]: from functools import singledispatch

In [17]: class MyClass:
...: def __init__(self, value):
...: self._value = value
...: def get_value(self):
...: return self._value
...:

In [18]: mc = MyClass("i am class of MyClass")

In [19]: dm = Decimal("12.12")

In [20]: dt = datetime.now()

In [21]: @singledispatch
...: def convert(o):
...: raise TypeError("can not convert type")
...:

In [22]: @convert.register(datetime)
...: def _(o):
...: return o.strftime("%Y-%m-%d %H:%M:%S")
...:

In [23]: @convert.register(Decimal)
...: def _(o):
...: return float(o)
...:

In [24]: @convert.register(MyClass)
...: def _(o):
...: return o.get_value()
...:

In [25]: class ExtendJSONEnxoder(json.JSONEncoder):
...: def default(self, obj):
...: try:
...: return convert(obj)
...: except TypeError:
# 最后调用父类是 default() 方法纯粹是为了触发异常。
...: return super(ExtendJSONEnxoder, self).default(obj)
...:

In [26]: data = {
...: "mc": mc,
...: "dm": dm,
...: "dt": dt
...: }

In [27]: json.dumps(data, cls=ExtendJSONEnxoder)
Out[27]: '{"mc": "i am class of MyClass", "dm": 12.12, "dt": "2018-12-26 16:07:10"}'

我们看另一种写法

1
2
3
4
5
6
# json字符串输出时,浮点数字错误处理函数
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
return float(o)
super(DecimalEncoder, self).default(o)

相比于上面的写法使用singledispatch更符合设计模式的规范。假如以后有了新的类型,不用再修改ExtendJSONEncoder 类,只需要添加适当的 singledispatch 方法就可以了, 比较pythonic

其他学习singledispatch的参考文章:

python基础-单分派泛函数singledispatch

python中functools.singledispatch的使用

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!