1. hello world

不论哪种编程语言,在你最开始学习时,都会给你一个在终端输出hello world的示例

print("hello world")

这已经成为一种惯例,最近在github上闲逛时,偶遇了一个特别有趣的项目,这个作者实现了一个模块,可以让你在python代码里,使用其他编程语言的语法和关键字来编写hello world , 就像下面这样

import anyprint

printf("printf %d\n", 10)       # c

fmt.Println("hello")            # go

cout << "Hello, C++!" << endl   # c++

这个项目的github地址是: https://github.com/kragniz/anyprint , 源码只有不到200行,真正核心的代码也只有20行。

2. 实现原理

刚看到这个库时,从它的示例代码中,我已经大致猜出了它的实现原理。

printf并不是python中的关键字,直接使用一定会报错的。这个函数一定是在anyprint 模块里实现了,但作者只是将anyprint引入(import anyprint ),而不是从anyprint模块里引入printf (from anyprint import printf),这说明,在anyprint模块里,已经将printf写入到内置作用域。

打开anyprint.py文件,在文件末尾有如下代码

def make_module(name, members):
    m = types.ModuleType(name)
    m.__name__ = name
    for k, v in members.items():
        if type(v) is dict:
            m.__dict__[k] = make_module(k, v)
        else:
            m.__dict__[k] = v
    return m


for k, v in prints.items():
    if type(v) is dict:
        globals()['__builtins__'][k] = make_module(k, v)
    else:
        globals()['__builtins__'][k] = v

globals函数以字典的形式返回全局作用域的变量,__builtins__ 所对应的value就是内置作用域模块,prints是一个字典,内容如下

prints = {
    # go
    'fmt': {
        'Println': print,
        'Printf': printf,
        'Print': printn,
    },

    # java
    'System': {
        'out': {
            'println': print,
            'printf': printf,
        },
        # Visual Basic .NET
        'Console': {
            'Write': printn,
            'WriteLine': print,
        },
    },

    # kotlin
    'println': print,

    # C like
    'printf': printf,
}

我以printf和fmt 为例,解释它如何工作的。在遍历prints时,如果key为printf,value是一个函数

printf = lambda *args: print(args[0] % args[1:], end='')

则执行

globals()['__builtins__'][k] = v   # 写入内置作用域

当你在脚本里使用printf时,如果局部作用域,内嵌作用域,全局作用域都找不到printf,则会去内置作用域里查找,而当前时刻,内置作用域里确实有pirntf,这个printf函数可以通过globals()[‘builtins’][‘printf’]获取到。

遍历prints时,如果key是fmt, 其value是一个字典,则执行

m.__dict__[k] = make_module(k, v)

我们来看make_module的实现

def make_module(name, members):
    m = types.ModuleType(name)
    m.__name__ = name
    for k, v in members.items():
        if type(v) is dict:
            m.__dict__[k] = make_module(k, v)
        else:
            m.__dict__[k] = v
    return m

函数首先会创建出一个名为fmt的模块,这个模块会被写入到内置模块,这样,就可以直接使用fmt了,fmt模块里,还有3个方法

{
    'Println': print,
    'Printf': printf,
    'Print': printn,
}

再次进行遍历,将Println,Printf, Print 写入模块m的__dict__字典中,value就是事先准备好的print, printf, printn 函数,这样当你在代码里执行

fmt.Println("hello")

首先在内置模块里找到fmt模块,然后在fmt模块的__dict__字典中找到Println函数来执行。

再来看java部分的实现

'System': {
        'out': {
            'println': print,
            'printf': printf,
        },
        # Visual Basic .NET
        'Console': {
            'Write': printn,
            'WriteLine': print,
        },
    },

嵌套层级更深,但原理是一样的,创建一个名为System的模块,写入内置作用域,在System的__dict__字典里有一个名为out的模块,在out模块的__dict__字典里有一个println函数。

作者巧妙的利用了python的内置作用域,或是直接将函数写入内置作用域,或是创建相应的模块供用户使用。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐