利用 MacroPy 的能力修改原始 AST,并不像使用 import macropy.activate 语句 那样简单,如果它不能以某种方式覆写 Python 的导入行为的话。幸运的是,Python 提供了 利用两种导入钩子(import hook)来拦截导入的方法。
• 元钩子(meta hook):它在任何其他import处理之前被调用。利用元钩子,你可
以覆写sys.path的处理方式,甚至是冻结模块(frozen module)和内置模块。
为了添加新的元钩子,必须向 sys.meta_path 列表中添加新的元路径查找器 (meta path finder)对象。
导入路径钩子(import path hook):它是作为 sys.path 处理的一部分被调用的。 如果遇到了与给定钩子相关联的路径项,则使用这种钩子。通过使用新的路径查找 器(path finder)对象扩展sys.path_hooks列表来添加导入路径钩子。
路径查找器和元路径查找器的实现细节在 Python 官方文档中都有详细说明(https://docs. python.org/3/reference/import.html)。如果你想要在这个层面上处理导入问题,那么官方文档 应该是你的首选资料。这是因为 Python 的导入机制相当复杂,任何尝试用几个段落进行总 结的方法最终不可避免会失败。你可以将本节当作这些可能做法的笔记,也可以当作更详 细资料的参考。
3.使用代码生成模式的项目
很难找到一个真正可用的库的实现,它依赖代码生成模式,而又不仅是一项实验或简
单的概念证明。造成这种情况的原因是显而易见的,如下所示。
• 对 exec()和 eval()函数必要的担心,因为不负责任的使用可能会造成真正的灾难。 • 成功的代码生成非常困难,因为它需要对语言特性的深入理解和优异的通用编程技能。 尽管有这些困难,但仍有一些项目成功地应用了这种方法,或者提高了性能,或者实
现了其他方法无法实现的事情。
(1)falcon 的编译路由器
falcon是一个极简的Python WSGI Web框架,用于构建快速又轻量级的API。它极力
推崇目前在 Web 上非常流行的 REST 架构风格。它是对其他大型框架(如 Django 或 Pyramid) 的很好的替代。对于其他致力于精简的微框架(如 Flask、Bottle 和 Web2py)而言,它也 是一个强大的竞争对手。
其特性之一就是非常简单的路由机制。它不像 Django 的 urlconf 提供的路由那样复 杂,也没有提供那么多的功能,但在大多数情况下,对于遵照 REST 架构设计的 API 都是 够用的。关于 falcon 的路由,最有趣的是实际的路由器是利用路由列表生成的代码来实现 的,这些路由列表被提供给定义 API 配置的对象。这种方法使路由的速度很快。
思考下面这个非常简短的 API 示例(来自于 falcon 的网上文档):
# sample.py
import falcon
import json
class QuoteResource:
def on_get(self, req, resp):
"""Handles GET requests"""
quote = {
'quote': 'I\'ve always been more interested in '
'the future than in the past.',
'author': 'Grace Hopper'
}
resp.body = json.dumps(quote)
api = falcon.API()
api.add_route('/quote', QuoteResource())
加粗的代码是对 api.add_route()方法的调用,简而言之,就是更新整个动态生成 的路由器代码树,利用 compile()进行编译,并利用 eval()生成新的路由查找函数。观 察 api._router._find()函数的__code__属性,可以发现它是从字符串中生成的,每 次调用 api.add_route()都会发生如下改变:
>>> api._router._find.__code__
<code object find at 0x00000000033C29C0, file "<string>", line 1> >>> api.add_route('/none', None)
>>> api._router._find.__code__
<code object find at 0x00000000033C2810, file "<string>", line 1>
(2)Hy
Hy(http://docs.hylang.org/)是完全用 Python 编写的 Lisp 方言。许多用 Python 实现其
他代码的类似项目,通常仅尝试标记代码的普通形式(作为类文件对象或字符串提供),并 将其解释为一系列显式的 Python 调用。与其他项目不同,Hy 可以被看作一种完全在 Python 运行环境中运行的的语言,就像 Python 一样。用 Hy 编写的代码可以使用现有的内置模块 和外部包,反之亦然。用 Hy 编写的代码导入到 Python 中。
为了将Lisp嵌入Python中,Hy将Lisp代码直接转换成Python抽象语法树。在Python 中导入 Hy 模块之后,利用注册过的导入钩子可以实现导入互操作性(interoperability)。所 有.hy 扩展名的模块都被看作 Hy 模块,都可以像普通 Python 模块那样导入。下面这个“hello world”程序就是用这种 Lisp 方言编写的:
;; hyllo.hy
(defn hello [] (print "hello world!"))
利用下列 Python 代码可以将其导入并执行:
>>> import hy
>>> import hyllo
>>> hyllo.hello()
hello world!
如果我们进一步钻研,尝试利用内置的 dis 模块反汇编 hyllo.hello,那么我们会 注意到,Hy 函数的字节码与它对应的纯 Python 代码的字节码没有很大差别,如下所示:
>>> import dis
>>> dis.dis(hyllo.hello)
2 0 LOAD_GLOBAL 3 LOAD_CONST
6 CALL_FUNCTION
0 (print)
1 ('hello world!')
1 (1 positional, 0 keyword pair)
9 RETURN_VALUE
>>> def hello(): print("hello world!")
>>> dis.dis(hello)