众所周知, Python 不支持函数重载,因此无法创建一个通用函数,针对不同的参数类型作出不同的处理。例如,创建一个函数 htmlize
,接受一个任意类型的 Python 对象作为参数,根据不同的参数类型,将其“序列化”为不同的 HTML 格式:
- 默认使用
<pre>
标签;
str
: 将换行符替换为’
\n’,然后将其放在<p>
标签中;
int
: 同时显示 10 进制和 16 进制,如: 12 (0xC);
list
: 以 HTML 列表的形式显示,每个元素按其类型分别格式化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| >>> htmlize({1, 2, 3}) '<pre>{1, 2, 3}</pre>' >>> htmlize(abs) '<pre><built-in function abs></pre>' >>> htmlize('Heimlich & Co.\n- a game') '<p>Heimlich & Co.<br>\n- a game</p>' >>> htmlize(42) '<pre>42 (0x2a)</pre>' >>> print(htmlize(['alpha', 66, {3, 2, 1}])) <ul> <li><p>alpha</p></li> <li><pre>66 (0x42)</pre></li> <li><pre>{1, 2, 3}</pre></li> </ul>
|
常见的做法是同if/elif/elif
结构,针对不同类型,分别调用相应的“序列化”函数。这样做主要的问题是缺乏灵活性和可扩展性。Python 3.4 引入的 functools.singledispatch
装饰器,可以使普通函数,成为通用函数(generic function),达到像 C++
函数重载的效果。Python 3.3及以下的版本,在 PyPI 上有 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
|
from functools import singledispatch from collections import abc import numbers import html
@singledispatch def htmlize(obj): return '<pre>{0}</pre>'.format(html.escape(repr(obj)))
@htmlize.register(str) def _(text): content = html.escape(text).replace('\n', '<br>\n') return '<p>{0}</p>'.format(content)
@htmlize.register(numbers.Integer) def _(num): return '{0} (0x{0:X})'.format(num) @htmlize.register(tuple) @htmlize.register(abc.MutableSequence) def _(seq): inner = '</li>\n<li>'.join(htmlize(item) for item in seq) return '<ul>\n<li>{0}</li>\n</ul>'.format(inner)
|
上述例子来自《O’Reilly Fluent Python》, PEP 443 对 singledispatch
有详细描述。