定义 __all__ 的简洁方法?

4

你好,我正在构建自己的包,并对__all__有一个问题。
除了显式地输入模块中的每个函数之外,是否有其他简洁的方式来定义__all__?
我觉得这很繁琐...

我正在尝试编写一些代码,用于包装常用库,例如numpy、pytorch、os等。问题是,当我导入我的包时,我使用的库也被导入了。
我想要导入我定义的每个函数/类,但我不想导入我在过程中使用的第三方库。
我在我的__init__.py中使用from .submodule import *,以便可以直接访问子模块中的函数。(就像我们可以直接从顶级包中访问函数一样,如np.sum()torch.sum()
我的子模块有很多函数,我想将它们全部导入到__init__.py中,但不包括我使用的第三方包。

我发现__all__定义了在调用from package import *时要导入的内容。
例如:

utils.py

__all__ = ['a']
def a():
    pass
def b():
    pass

__init__.py

from .utils import *

并且

>>> 导入包
>>> 包.a()

>>> 包.b()
名称错误:'package.b' 未定义

我想要的是这样的结果

__all__ = Some_neat_fancy_method()

我尝试了locals()和dir(),但是一路上迷失了方向。你有什么建议吗?


7
如果您想包含所有内容,可以简单地省略 __all__ - juanpa.arrivillaga
2
使用 * 导入是错误的做法。我建议您按名称导入,您可以在 __init__ 文件中使用 __all__ 并定义要使其可用的包。 - sahasrara62
2
我看到大家都有些困惑。我加了更多的解释。我正在构建一个基于其他第三方库的包,问题是,它们也被导入了。我想要导入我定义的每个函数/类,但我不想导入我在过程中使用的第三方库。 - jsyoo61
4个回答

3
正如其他人所指出的,__all__ 的整个目的是明确指定哪些内容可以被星号导入。默认情况下一切都可以被导入。如果您真的想要指定不暴露的内容,则可以使用一个小技巧,将所有模块都包含在__all__中,然后删除要排除的模块。
例如:
def _exclude(exclusions: list) -> list:
    import types

    # add everything as long as it's not a module and not prefixed with _
    functions = [name for name, function in globals().items()
                 if not (name.startswith('_') or isinstance(function, types.ModuleType))]

    # remove the exclusions from the functions
    for exclusion in exclusions:
        if exclusion in functions:
            functions.remove(exclusion)

    del types  # deleting types from scope, introduced from the import
    return functions


# the _ prefix is important, to not add these to the __all__
_exclusions = ["function1", "function2"]
__all__ = _exclude(_exclusions)

当然,您可以重新使用此代码来包含所有不是函数或以_为前缀的内容,但是这样做没有什么用处,因为如果您不指定__all__,则在星号导入中包括了所有内容,所以我认为最好包括exclusion概念。这样,您就可以简单地告诉它排除特定函数。

1
除了显式地输入模块中的每个函数,还有其他简便的方法来定义“all”吗? 没有内置的方法。但是手动定义“__all__”基本上就是全部内容,如果您想在“__all__”中包含所有内容,可以什么都不做: 如果未定义“__all__”,则语句“from sound.effects import *”确保已导入sound.effects包(可能运行任何初始化代码在“__init__.py”中),然后导入包中定义的任何名称。 “__all__” 的整个重点是通过星号导入限制要“导出”的内容。Python 没有真正的方法能够知道这一点,除非您告诉它每个符号是否应该存在。

0
一个简单的解决方法是使用下划线作为前缀来给你的引入取别名。任何有前导下划线的内容都会被 from x import * 样式的引入排除掉。
import numpy as _np
import pandas as _pd

def my_fn():
    ...

0
我会定义一个名为"export"的装饰器,并对我想要添加到所有的函数、类等进行装饰。这个装饰器会将项目添加到一个"exports"列表中,然后

所有 = exports


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接