管理__all__的导出装饰器

MvG*_*MvG 5 python visibility module python-module package

一个合适的Python模块将列出名为的列表中的所有公共符号.管理该列表可能很繁琐,因为您必须两次列出每个符号.当然有更好的方法,可能使用装饰器,因此只需将导出的符号注释为.__all__@export

你会怎么写这样的装饰师?我确定有不同的方法,所以我希望看到几个答案,其中包含足够的信息,用户可以将这些方法相互比较.

MvG*_*MvG 6

使用装饰器向 __all__ 添加名称是一个好习惯吗?Ed L建议将以下内容包含在某些实用程序库中:

import sys

def export(fn):
    """Use a decorator to avoid retyping function/class names.

    * Based on an idea by Duncan Booth:
      http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a
    * Improved via a suggestion by Dave Angel:
      http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1
    """
    mod = sys.modules[fn.__module__]
    if hasattr(mod, '__all__'):
        name = fn.__name__
        all_ = mod.__all__
        if name not in all_:
            all_.append(name)
    else:
        mod.__all__ = [fn.__name__]
    return fn
Run Code Online (Sandbox Code Playgroud)

我们已调整名称以匹配其他示例。在本地实用程序库中使用它,您只需编写

from .utility import export
Run Code Online (Sandbox Code Playgroud)

然后开始使用@export。只需一行惯用的 Python,就没有比这更简单的了。不利的一面是,该模块确实需要使用__module__属性和sys.modules缓存来访问该模块,这两种设置在一些深奥的设置中都可能会出现问题(例如自定义导入机制,或者包装来自另一个模块的函数以在此模块中创建函数)。模块)。

Barry Warsawatpublic python 部分做了类似的事情。它也提供了一些基于关键字的语法,但装饰器变体依赖于上面使用的相同模式。

Aaron Hall这个很好的答案提出了非常相似的东西,多了两行代码,因为它不使用__dict__.setdefault. __dict__如果由于某种原因操作模块出现问题,这可能会更好。


MvG*_*MvG 5

您可以简单地在模块级别声明装饰器,如下所示:

__all__ = []

def export(obj):
    __all__.append(obj.__name__)
    return obj
Run Code Online (Sandbox Code Playgroud)

如果您只在单个模块中使用它,那么这是完美的。在 4 行代码中(可能还加上一些用于典型格式化实践的空行),在不同的模块中重复此操作并不会太昂贵,但在这些情况下确实感觉像是代码重复。