如前所述动态代码生成是最难的代码生成方法。Python 中有一些工具可以让你生成并执行代码甚至可以对已编译的代码对象进行修改。关于这一点可以写一本完整的书即使这样也不能将这一话题完全写完。许多项目例如后面提到的 Hy都表明利用代码生成技术甚至整个语言都可以用Python 重新实现。这说明其可能性几乎是无限的。知道了这个主题的范围之广以及它充满各种易犯的错误我甚至不会尝试给出关于如何用这种方法创建代码的详细建议也不会提供有用的代码示例。无论如何如果你打算独自深入研究这一领域知道其用法对你可能是有用的。因此可以将本节仅作为对进一步学习的起点的简要总结。大多数内容都伴随有许多警告以防你在自己的项目中迫不及待地调用 exec()和 eval()。exec、eval 和 compilePython 提供了 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()仍有许多小细节由于内容太多我们这里不会列出但它们会影响你的应用程序的工作方式使其与你的预期不同。ArminRonacher 写过一篇很棒的文章列出了其中最重要的细节文章标题为Be careful with execand eval in Python参见 http://lucumr.pocoo.org/2011/2/1/exec-in-python/)。尽管有这些吓人的警告但有些情况下使用 exec()和 eval()是非常合理的。关于何时使用它们流行的说法是“到时你自然会知道。”换句话说即使你有一丝的怀疑也不应该使用它们而应该尝试寻找其他解决方法。与其他 Python 高级特性类似元类非常灵活很容易被滥用。虽然类的调用签名相当严格但 Python 并不强制要求返回参数的类型。只要它接受调用的传入参数并且有必要的属性那么它可以是任何内容。这种任何内容-任何地点anything-anywhere的一个对象就是 unittest.mock 模块提供的 Mock 类。Mock 不是元类也不继承自 type 类。它在实例化时也不返回类对象。但它可以作为元类关键字参数包含在类定义中而且这不会引发任何问题虽然这么做没有任何意义from unittest.mock import Mockclass Nonsense(metaclassMock): # pointless, but illustrative… pass…Nonsense当然上一个例子完全没有任何意义尝试对这样的 Nonsense 伪类进行初始化也会失败。但知道可能出现这样的情况很重要因为使用了 metaclass 类型却没有创建 type的子类这样的问题有时很难发现与理解。为了证明这句话下面是尝试创建 Nonsense类的新实例时对引发异常的回溯Nonsense()Traceback (most recent call last):File “”, line 1, inFile “/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py”, line 917, in __call __return _mockself.mock_call(*args, **kwargs)File “/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py”, line 976, in _mock_callresult next(effect)StopIteration
Python 高手编程系列五百三十:一些关于代码生成的提示
如前所述动态代码生成是最难的代码生成方法。Python 中有一些工具可以让你生成并执行代码甚至可以对已编译的代码对象进行修改。关于这一点可以写一本完整的书即使这样也不能将这一话题完全写完。许多项目例如后面提到的 Hy都表明利用代码生成技术甚至整个语言都可以用Python 重新实现。这说明其可能性几乎是无限的。知道了这个主题的范围之广以及它充满各种易犯的错误我甚至不会尝试给出关于如何用这种方法创建代码的详细建议也不会提供有用的代码示例。无论如何如果你打算独自深入研究这一领域知道其用法对你可能是有用的。因此可以将本节仅作为对进一步学习的起点的简要总结。大多数内容都伴随有许多警告以防你在自己的项目中迫不及待地调用 exec()和 eval()。exec、eval 和 compilePython 提供了 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()仍有许多小细节由于内容太多我们这里不会列出但它们会影响你的应用程序的工作方式使其与你的预期不同。ArminRonacher 写过一篇很棒的文章列出了其中最重要的细节文章标题为Be careful with execand eval in Python参见 http://lucumr.pocoo.org/2011/2/1/exec-in-python/)。尽管有这些吓人的警告但有些情况下使用 exec()和 eval()是非常合理的。关于何时使用它们流行的说法是“到时你自然会知道。”换句话说即使你有一丝的怀疑也不应该使用它们而应该尝试寻找其他解决方法。与其他 Python 高级特性类似元类非常灵活很容易被滥用。虽然类的调用签名相当严格但 Python 并不强制要求返回参数的类型。只要它接受调用的传入参数并且有必要的属性那么它可以是任何内容。这种任何内容-任何地点anything-anywhere的一个对象就是 unittest.mock 模块提供的 Mock 类。Mock 不是元类也不继承自 type 类。它在实例化时也不返回类对象。但它可以作为元类关键字参数包含在类定义中而且这不会引发任何问题虽然这么做没有任何意义from unittest.mock import Mockclass Nonsense(metaclassMock): # pointless, but illustrative… pass…Nonsense当然上一个例子完全没有任何意义尝试对这样的 Nonsense 伪类进行初始化也会失败。但知道可能出现这样的情况很重要因为使用了 metaclass 类型却没有创建 type的子类这样的问题有时很难发现与理解。为了证明这句话下面是尝试创建 Nonsense类的新实例时对引发异常的回溯Nonsense()Traceback (most recent call last):File “”, line 1, inFile “/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py”, line 917, in __call __return _mockself.mock_call(*args, **kwargs)File “/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py”, line 976, in _mock_callresult next(effect)StopIteration