什么时候用Python完成元编程

2024-09-30 01:33:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我读过一篇文章,似乎说Lisp、Scheme和类似Lisp的宏的元编程发生在编译时:http://tratt.net/laurie/research/pubs/html/tratt__compile-time_meta-programming_in_a_dynamically_typed_oo_language/。在

它似乎还指出,像Python这样的动态语言不使用太多的编译时元编程。我知道在某种程度上,Java可以使用类加载器进行编译时元编程。在使用元类和修饰符的Python元编程中,以及使用诸如type()、isInstance()等方法进行反射的Python元编程中……这都是运行时的元编程,还是背后还有更多的元编程?在


Tags: httpnettimehtml编程文章metascheme
1条回答
网友
1楼 · 发布于 2024-09-30 01:33:12

简短的说法是:是的,decorator、元类等主要是在运行时发生的事情。在

这也意味着,对于理解Python中的元编程,SmallTalk通常比Lisp更好


长版本有点复杂。在

Python中的“编译时”是指将函数定义、类定义和模块本身编译为字节码“运行时”包括解释这些实体。2

特别是,像defclass语句这样的语句是(编译为)运行时代码的,它们可以像其他语句一样执行。在


例如,考虑以下模块:

@spam
def eggs():
    print(3)

print(3)主体被编译成一些字节码,在执行时,它查找print,并用参数3调用它。然后可以将字节码视为常量。在

然后模块主体被编译成类似这样的伪代码:

^{pr2}$

当您import该模块(或将其作为脚本运行)时,编译的代码将在运行时执行。所以,这时就有人打电话给装饰工了。在


同样,考虑一下:

class Spam(metaclass=MetaSpam):
    def eggs(self):
        pass

首先,pass被编译成字节码,它只返回None,它可以被视为常量。在

接下来,这个类主体被编译成字节码。由于类主体只有def语句,因此字节码的作用与此等效:

eggs = FunctionType('eggs', eggs_bytecode_constant, ('self',), other_stuff))

接下来,该class语句被编译成字节码,执行如下操作:

_namespace = {}
exec(Spam_bytecode_constant, _namespace)
Spam = MetaSpam('Spam', (object,), _namespace)

然后,当您import该模块(或将其作为脚本运行)时,该字节码将被执行。因此,这时调用元类,创建类对象。在


这意味着您几乎可以忽略编译时发生的事情。3

如果您想直接调用type(或自定义元类),则会获得与class语句完全相同的效果。您甚至可以手动从字节码对象中构造函数对象,并获得与def语句或lambda表达式完全相同的效果。而且,您可以在函数或类被创建后对其进行修改,例如,通过Spam.cheese = cheese向类添加新方法与直接在class语句中定义它们没有什么不同。4

这也意味着在Python中反射并不是什么神奇的东西。对象在public属性中携带它们的类型信息,^{}模块所做的工作与解释器对相同属性所做的工作几乎相同。在

但是,另一方面,这意味着一些用Lisp宏容易做的事情,比如将表达式的AST而不是表达式的值作为参数,在Python元编程中是不可能的。在


好吧,我说这是不可能的,但是……如果你想用Python做Lisp风格的元编程,实际上你也可以做到。它只意味着编写和安装import hooks5

通常,在Python中,import查找源文件,用bytes.decode将其解码为文本,用tokenize模块标记它,用ast.parse解析标记,并用compile编译结果。所有这些部分都暴露在Python代码中,而且(在3.4以上版本中)整个导入系统本身都是用Python编写的,使用的模块与您自己可以使用的相同。在

因此,导入钩子可以安装一个自定义加载程序,该加载程序将像默认加载程序一样进行解码、标记化和解析,然后像Lisp样式的宏那样修改AST,然后像默认加载程序一样编译并返回结果。在

如果您对此感兴趣,应该查看MacroPy。在


1。事实上,IIRC、Forman的第一版和Danforth的SmallTalk书让元类工作以及Danforth的另一篇论文是对Python元程序的主要影响设计。

2。在交互模式下,Python一次编译并执行一个语句,将一些东西混合在一起,但思想没有太大不同。

3。实际上,不同的实现可以选择在编译时比CPython做更多或更少的工作,只要语义最终相同。

4。除了一些微妙的问题,例如def语句在上面的那个_namespace内与{}内部执行的方式不同,这可能会影响super()

5。最初的PEP很好地涵盖了历史和基本原理,但没有涵盖在现代Python中编写和安装钩子的方法。为此,请阅读the import system上的参考文档,并按照importlib包的链接进行操作。

相关问题 更多 >

    热门问题