将Python包中的符号导入调用者命名空间

4 投票
4 回答
7156 浏览
提问于 2025-04-15 23:27

我有一个小的内部领域特定语言(DSL),它是用一个Python文件写的,随着时间的推移,它变得越来越复杂,我想把内容分散到多个不同的目录和文件中。

现在新的目录结构看起来是这样的:

dsl/
    __init__.py
    types/
        __init__.py
        type1.py
        type2.py

每个类型的文件里都包含一个类(比如说 Type1)。

我遇到的问题是,我希望使用这个DSL的代码尽可能简单,比如说:

import dsl
x = Type1()
...

这意味着所有重要的符号应该直接在用户的命名空间中可用。我尝试更新顶层的 __init__.py 文件,以导入相关的符号:

from types.type1 import Type1
from types.type2 import Type2
...
print globals()

输出显示符号确实被正确导入了,但在调用代码中(也就是执行 import dsl 的代码)仍然没有这些符号。我认为问题在于这些符号实际上是被导入到了 'dsl' 的命名空间中。我该如何改变这个,让类也能直接在调用者的命名空间中可用呢?

4 个回答

1

@Eli, @Daniel,谢谢你们的“恍然大悟”回答。我差不多明白了,但还需要一点额外的提示...

其实解决方案分为两个步骤:第一步是使用包的初始化器,把“导出的”二级符号拉到顶层的 dsl 包里(这一点我已经做到了),第二步是用 from dsl import * 把这些符号引入到调用者的代码中。这是合理的,因为调用者应该掌控自己想要引入到命名空间里的内容。

一般来说,from pkg import * 这种写法是不太被推荐的,但在这种情况下,我觉得这是个合理的解决方案,因为我这个包导出的符号数量会很有限。

2

我会这样做

在 dsl/init__.py 文件中,添加

def import_symbols(namespace):
    namespace['type1'] = dsl.types.type1
    namespace['type2'] = dsl.types.type2

从调用者那里,执行

import dsl

dsl.import_symbols(globals())

不仅可以从第二级包中导入符号到你当前的命名空间,通过定义你自己的 import_symbols(),你还可以更明确地控制要导入哪些符号,而不是用 import * 一下子导入所有东西。

4

你需要这样说

from dsl import *

撰写回答