代码之家  ›  专栏  ›  技术社区  ›  Gakuo

什么时候用Python完成元编程

  •  0
  • Gakuo  · 技术社区  · 6 年前

    我读过一篇文章,似乎说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()等方法的反射中……这都是运行时的元编程,还是后面还有更多的元编程?

    1 回复  |  直到 6 年前
        1
  •  4
  •   abarnert    6 年前

    简短的版本是:是的,decorators、元类等主要是在运行时发生的事情。

    这也意味着SmallTalk通常比Lisp更适合理解Python中的元编程。 1个


    长版有点复杂。

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

    尤其是像 def class 语句是(编译成)运行时代码,与其他任何语句一样执行。


    例如,考虑这个模块:

    @spam
    def eggs():
        print(3)
    

    print(3) body被编译成一些字节码,在执行时查找 print 把它和论点联系起来 3 . 然后可以将该字节码视为常量。

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

    当你 import 编译代码在运行时执行的模块(或作为脚本运行)。所以,就在那个时候,装饰师被叫来了。


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

    首先,那 pass 被编译成字节码,只返回 None ,可以作为常数处理。

    接下来,该类主体被编译成字节码。因为班上只有 定义 语句中,字节码的作用等同于:

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

    下一个,那个 语句被编译为字节码,执行如下操作:

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

    进口


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

    你想打电话给我吗 type (或自定义元类)直接获得与 陈述。您甚至可以手动从字节码对象构造函数对象,获得与 陈述或 lambda 表达。在函数或类被创建之后,您可以修改它。例如,通过 Spam.cheese = cheese 结果与你在 陈述。 4个

    这也意味着反射在Python中并不是什么神奇的东西。对象在公共属性中携带其类型信息,而 inspect 模块所做的工作与解释器使用相同属性所做的工作几乎相同。


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

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

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

    MacroPy .


    一。事实上,IIRC,福尔曼和丹福斯的小册子的第一版 使用元类 Danforth的另一篇论文是对Python元编程设计的主要影响。

    2。在交互模式下,Python编译然后一次执行一个语句,稍微混合一些,但是这些想法并没有太大的不同。

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

    定义 _namespace 上面vs.里面 globals() ,可能会影响 super() .

    the import system 并按照链接 importlib 包裹。