假设我们在开发一个调试Web应用的工具,我们想生成HTML,显示不同类型的Python对象。我们可能会编写这样的函数:
import html def htmlize(obj): content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content)
这个函数适用于任何Python类型,但是现在我们想做个扩展,让它使用特别的方式显示某些类型。
str:把内部的换行符替换为'<br>\n';不使用<pre>,而是使用<p>。
int:以十进制和十六进制显示数字。
list:输出一个HTML列表,根据各个元素的类型进行格式化。
因为Python不支持重载方法或函数,所以我们不能使用不同的签名定义htmlize的变体,也无法使用不同的方式处理不同的数据类型。在Python中,一种常见的做法是把htmlize变成一个分派函数,使用一串if/elif/elif,调用专门的函数,如htmlize_str、htmlize_int,等等。这样不便于模块的用户扩展,还显得笨拙:时间一长,分派函数htmlize会变得很大,而且它与各个专门函数之间的耦合也很紧密。Python 3.4新增的functools.singledispatch装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。使用@singledispatch装饰的普通函数会变成泛函数(generic function):根据第一个参数的类型,以不同方式执行相同操作的一组函数。
示例7-21 singledispatch创建一个自定义的htmlize.register装饰器,把多个函数绑在一起组成一个泛函数
from functools import singledispatch from collections import abc import numbers import html @singledispatch ➊ def htmlize(obj): content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content) @htmlize.register(str) ➋ def _(text): ➌ content = html.escape(text).replace('\n', '<br>\n') return '<p>{0}</p>'.format(content) @htmlize.register(numbers.Integral) ➍ def _(n): return '<pre>{0} (0x{0:x})</pre>'.format(n) @htmlize.register(tuple) ➎ @htmlize.register(abc.MutableSequence) def _(seq): inner = '</li>\n<li>'.join(htmlize(item) for item in seq) return '<ul>\n<li>'+inner+'</li>\n</ul>'
❶ @singledispatch标记处理object类型的基函数。❷ 各个专门函数使用@«base_function».register(«type»)装饰。❸ 专门函数的名称无关紧要;_是个不错的选择,简单明了。❹ 为每个需要特殊处理的类型注册一个函数。numbers.Integral是int的虚拟超类。❺ 可以叠放多个register装饰器,让同一个函数支持不同类型。
只要可能,注册的专门函数应该处理抽象基类(如numbers.Integral和abc.MutableSequence),不要处理具体实现(如int和list)。这样,代码支持的兼容类型更广泛。例如,Python扩展可以子类化numbers.Integral,使用固定的位数实现int类型。
[插图]使用抽象基类检查类型,可以让代码支持这些抽象基类现有和未来的具体子类或虚拟子类。
singledispatch机制的一个显著特征是,你可以在系统的任何地方和任何模块中注册专门函数。如果后来在新的模块中定义了新的类型,可以轻松地添加一个新的专门函数来处理那个类型。此外,你还可以为不是自己编写的或者不能修改的类添加自定义函数。singledispatch是经过深思熟虑之后才添加到标准库中的,它提供的特性很多,这里无法一一说明。这个机制最好的文档是“PEP 443—Single-dispatch generic functions”。[插图]
@singledispatch不是为了把Java的那种方法重载带入Python。在一个类中为同一个方法定义多个重载变体,比在一个函数中使用一长串if/elif/elif/elif块要更好。但是这两种方案都有缺陷,因为它们让代码单元(类或函数)承担的职责太多。@singledispath的优点是支持模块化扩展:各个模块可以为它支持的各个类型注册一个专门函数。
装饰器是函数,因此可以组合起来使用(即,可以在已经被装饰的函数上应用装饰器,如示例7-21所示)。
def escape(s, quote=True):标签:流畅,函数,python,register,分派,Python,singledispatch,类型,htmlize From: https://www.cnblogs.com/bravesunforever/p/17353749.html
"""
Replace special characters "&", "<" and ">" to HTML-safe sequences.
If the optional flag quote is true (the default), the quotation mark
characters, both double quote (") and single quote (') characters are also
translated.
"""
s = s.replace("&", "&") # Must be done first!
s = s.replace("<", "<")
s = s.replace(">", ">")
if quote:
s = s.replace('"', """)
s = s.replace('\'', "'")
return s