Python 高手编程系列一千一百三十三:一些关于代码生成的提示

如前所述,动态代码生成是最难的代码生成方法。Python 中有一些工具可以让你生成
并执行代码,甚至可以对已编译的代码对象进行修改。关于这一点可以写一本完整的书,
即使这样也不能将这一话题完全写完。
许多项目(例如后面提到的 Hy)都表明,利用代码生成技术,甚至整个语言都可以用
Python 重新实现。这说明其可能性几乎是无限的。知道了这个主题的范围之广以及它充满
各种易犯的错误,我甚至不会尝试给出关于如何用这种方法创建代码的详细建议,也不会
提供有用的代码示例。
无论如何,如果你打算独自深入研究这一领域,知道其用法对你可能是有用的。因此,
可以将本节仅作为对进一步学习的起点的简要总结。大多数内容都伴随有许多警告,以防
你在自己的项目中迫不及待地调用 exec()和 eval()。
exec、eval 和 compile
Python 提供了 3 个内置函数,用于手动执行、求值和编译任意 Python 代码。
• exec(object, globals, locals):这一函数允许你动态执行 Python 代码。
object 应该一个字符串或代码对象(参见 compile()函数)。globals 和
locals 参数为所执行的代码提供全局的和局部的命名空间,这二者是可选的。如
果没有提供这两个参数,那么就在当前作用域中执行代码。如果提供了这两个参数,
globals 必须是字典,而 locals 可以是任何映射对象。其返回值始终为 None。
• eval(expression, globals, locals):这一函数用于对给定表达式进行求
值并返回其结果。它与 exec()类似,但接受的 expression 应该是单一 Python
表达式,而不是一系列语句。它返回表达式求值的结果。
• compile(source, filename, mode):这一函数将源代码编译成代码对象或
AST 对象。要编译的代码在 source 参数中作为字符串提供。filename 应该是
读取代码的文件。如果源文件是动态创建的,因此没有相关联的文件,那么一般用
作为它的值。mode 应该是 exec(一系列语句)、eval(单一表达式)
或 single(单一交互式语句,例如在 Python 交互式会话中)。
如果你尝试动态生成代码,exec()和 eval()函数是最容易上手的,因为它们可以对
字符串进行操作。如果你已经知道如何用 Python 编程,那么你可能知道如何用编程的方式
正确地生成工作源代码。我希望你知道这一点。
对于元编程而言,最有用的显然是 exec(),因为它可以执行任意 Python 语句的序列。
看到“任意”两个字,你应该感到警觉。即使是 eval(),只允许对高明的程序员负责的
表达式求值(用户自行输入),也会导致严重的安全漏洞。注意,使 Python 解释器崩溃是
你应该担心的最不恐怖的情形。由于不负责任地使用 exec()和 eval(),从而引入远程
执行漏洞,这可能会有损你专业开发者的形象,甚至会让你失去工作。
即使输入的内容可信,但关于 exec()和 eval()仍有许多小细节,由于内容太多我
们这里不会列出,但它们会影响你的应用程序的工作方式,使其与你的预期不同。Armin
Ronacher 写过一篇很棒的文章,列出了其中最重要的细节,文章标题为:Be careful with exec
and eval in Python(参见 http://lucumr.pocoo.org/2011/2/1/exec-in-python/)。
尽管有这些吓人的警告,但有些情况下使用 exec()和 eval()是非常合理的。关于
何时使用它们,流行的说法是:“到时你自然会知道。”换句话说,即使你有一丝的怀疑,也不应该使用它们,而应该尝试寻找其他解决方法。

与其他 P
ython 高级特性类似,元类非常灵活,很容易被滥用。虽然类的调用签名相当严格,但 Python 并不强制要求返回参数的类型。只要它接受调用的传入参数,并且有必要
的属性,那么它可以是任何内容。
这种任何内容-任何地点(anything-anywhere)的一个对象就是 unittest.mock 模块
提供的 Mock 类。Mock 不是元类,也不继承自 type 类。它在实例化时也不返回类对象。
但它可以作为元类关键字参数包含在类定义中,而且这不会引发任何问题,虽然这么做没
有任何意义:

from unittest.mock import Mock
class Nonsense(metaclass=Mock): # pointless, but illustrative
… pass

Nonsense

当然,上一个例子完全没有任何意义,尝试对这样的 Nonsense 伪类进行初始化也会
失败。但知道可能出现这样的情况很重要,因为使用了 metaclass 类型却没有创建 type
的子类,这样的问题有时很难发现与理解。为了证明这句话,下面是尝试创建 Nonsense
类的新实例时对引发异常的回溯:
Nonsense()
Traceback (most recent call last):
File “”, line 1, in
File “/Library/Frameworks/Python.framework/Versions/3.5/lib/
python3.5/unittest/mock.py”, line 917, in __call __
return _ mock self. mock _call(*args, **kwargs)
File “/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unit
test/mock.py”, line 976, in _ mock _call
result = next(effect)
StopIteration

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值