1. Plugin 与 Python
插件的历史最早可追溯至 1970 年代, 它是一种程序组件, 通过和应用程序的互动, 为应用程序增加一些所需要的特定的功能[维基 https://zh.wikipedia.org/wiki/插件 ]. 插件允许第三方开发者对已有的程序功能进行扩展和完善, 具体的例子包括音频播放软件的解码器, 浏览器的视频播放插件等. 插件需要按照一定的接口规范与应用程序互动, 这个规范是调用它的应用程序定义的.
为了在实践上应用, 我看了两篇搜索比较靠前的博客文章, 它们的代码比较精简, 都是通过 python package 来实现的.
本文组织如下. 第二部分介绍 python package; 第三部分介绍上面两篇博客中实现插件的方法.
2. Python packages
Package 是 python 用名字空间组织 module 的一种方法, 通过 "." 将各级路径连接起来, 以 "A.B.C" 的形式组成层次结构. 每一个 python 文件是一个 module, 包含 module 的目录可以组成一个 package, 包含 package 的目录又可以进一步组成更高级的 package.
2.1 package 的基本结构
不是所有的目录都会被解释为 package, 其充要条件是目录内包含一个名为__init.py 的文件, 这个文件可以是空文件, 也可以包含一些定义和初始化代码, 包含该文件的目录可以被 import. 如下目录可以被作为 package 使用, 本例中除了__init.py 文件外, 还包含三个 module.
- myprint/
- |---__init__.py
- |---print_int.py
- |---print_float.py
- |---print_complex.py
因为 myprint 目录下有__init__.py 文件, myprint 是一个 package, 可以在 python 代码或命令行中 import 它及它包含的 module.
import myprint # import 了一个 package
或
import myprint.print_int # import 了 package 下的一个 module
或
- from myprint import print_int # import 了 package 下的一个 module, 引用时可以不带 package name
- 2.2 subpackage
Packages 可以形成多层嵌套的结构, tensorflow 代码中经常看到 import 多层的情况. 下例中 tensorflow,python,framework 都是上一层 namespace 中的 package, 只有 ops 是 module.
import tensorflow.python.framework.ops
如果有 import A.B.C.D, 只有 D 可以是 module,A,B,C 都必须是 package.
2.3 __init__.py 文件
文件__init.py 让 python 将其所属目录视为 package, 该文件可以是空文件, 也可以包含 python 代码, 文件内的代码在 package 第一次被进程 import 时执行一次. 文件__init__.py 中定义的函数, 变量, 可以通过 package name 被外部引用. 如果上面例子中 myprint/__init.py 包含如下代码, 则 default_int 和 default_float 两个名字可以通过 myprint.default_int 和 myprint.default_float 访问.
- __all__ = ['print_int', 'print_float']
- default_int = 0
- default_float = 0.0
- import myprint
- myprint.print_int
- myprint.print_float
如果 import 的是一个 subpackage, 则路径上所有 package 的__init__.py 文件会被依次执行. 例如, 当 import A.B.C.D 时, A package,B package,C package 会被先后初始化, 如果 D 也是 package, 则 D package 也会被初始化.
__init__.py 文件中还可以定义__all__变量, 如上面代码中有__all__ = ['print_int', 'print_float'], 当通过代码 from myprint import * 导入 package 时, 只有__all__中定义的所有 name 会被导入, 而不是把 myprint / 目录下的所有东西都导入.__all__中定义的 name 是 package 下的 module 或 subpackage.
下面介绍的两种实现插件的方法, 都用到了__init__.py 文件的__all__变量.
- text_processor
- |---__init__.py
- |---text_processor.py
- |---plugins
- |---__init__.py
- |---clean_dupspace.py
- |---clean_htmltag.py
- |---clean_punctuation.py
- |---clean_q2b.py
- class TextProcessor(object):
- PLUGIN_NAMES = []
- PLUGINS = {}
- def process(self, text, plugins=()):
- if plugins is ():
- for plugin_name in self.PLUGIN_NAMES:
- text = self.PLUGINS[plugin_name]().process(text)
- else:
- for plugin_name in plugins:
- text = self.PLUGINS[plugin_name]().process(text)
- return text
- @classmethod
- def plugin_register(cls, plugin_name):
- def wrapper(plugin):
- cls.PLUGINS.update({plugin_name:plugin})
- cls.PLUGIN_NAMES.append(plugin_name)
- return plugin
- return wrapper
- from ..textProcessor import TextProcessor
- import re
- re_space = re.compile(u'\s+')
- @TextProcessor.plugin_register('cleanDupSpace')
- class CleanDupSpace(object):
- def process(self, text):
- cleanText = re_space.sub(u' ', text)
- cleanText = cleanText.replace(u"\u3000", ' ')
- cleanText = cleanText.strip()
- return cleanText
- class CleanDupSpace(object):
- def process(self, text):
- cleanText = re_space.sub(u' ', text)
- cleanText = cleanText.replace(u"\u3000", ' ')
- cleanText = cleanText.strip()
- return cleanText
- CleanDupSpace = TextProcessor.plugin_register('cleanDupSpace')(CleanDupSpace)
- from text_processor.text_processor import TextProcessor
- tp = TextProcessor()
- cleanText = tp.process(rawText)
来源: https://www.cnblogs.com/terencezhou/p/10276167.html