在__init__.py中递归填充__all__

时间:2013-02-15 07:35:52

标签: python flask

我正在使用以下代码填充模块__all__中的__init__.py,如果有更有效的方法,我就会徘徊。有什么想法吗?

import fnmatch
import os

__all__ = []
for root, dirnames, filenames in os.walk(os.path.dirname(__file__)):
    root = root[os.path.dirname(__file__).__len__():]
    for filename in fnmatch.filter(filenames, "*.py"):
        __all__.append(os.path.join(root, filename[:-3]))

2 个答案:

答案 0 :(得分:2)

您可能不应该这样做:import的默认行为非常灵活。如果您不想自动导出模块(或任何其他变量),请为其命名,以_开头,python不会导出它。这是标准的python方式,重新发明轮子被认为是unpythonic。另外,不要忘记模块之外的其他东西可能需要导出;设置__all__后,您还需要查找并导出它们。

仍然,您要问如何最好地生成可导出模块的列表。由于您无法导出不存在的内容,我只需检查您的主模块中已知的模块:

basedir = os.path.dirname(__file__)
for m in sys.modules:
    if m in locals() and not m.startswith('_'): # Only export regular names
        mod = locals()[m]
        if '__file__' in mod.__dict__  and mod.__file__.startswith(basedir):
            print m

sys.modules包含python已加载的每个模块的名称,包括许多尚未导出到主模块的模块 - 因此我们检查它们是否在locals()

这比扫描文件系统更快,并且比假设目录树中的每个.py文件以某种方式最终作为顶级子模块更强大。当然,您应该在__init__.py的末尾附近运行此代码,此时所有内容都已加载。

答案 1 :(得分:0)

我处理一些带有子软件包和子模块的复杂软件包。我喜欢逐个模块地控制它。我使用了一个名为auto-all的简单程序包,该程序包很容易(完全公开-我是作者)。

https://pypi.org/project/auto-all/

这是一个例子:

from auto_all import start_all, end_all

# Define some internal stuff

start_all(globals())

# Define some external stuff

end_all(globals())

我使用此方法的原因主要是由于导入。如alexis所述,您可以通过在对象名称前添加下划线来隐式地使事物私有,但是对于导入的对象而言,这可能会变得凌乱或不切实际。考虑以下代码:

from pyspark.sql.session import SparkSession

如果这出现在您的模块中,则将隐式使SparkSession可从模块外部访问。另一种方法是在所有导入的项目前加上下划线,例如:

from pyspark.sql.session import SparkSession as _SparkSession

这也不理想,因此(我知道)手动管理__all__是管理外部可用内容的唯一方法。

您可以通过显式设置__all__变量的内容(的pythonic方式)轻松地做到这一点,但是在管理大量对象时这可能变得很乏味,并且如果开发人员添加了新对象并且没有通过添加到__all__变量来公开对象,也会导致问题。这种类型的事情可能会通过代码审查。使用简单的辅助函数来管理变量内容将使此操作变得更加容易。