Python闭包是否是`__all__`的良好替代品?

2024-04-25 23:35:46 发布

您现在位置:Python中文网/ 问答频道 /正文

使用闭包而不是__all__来限制Python模块公开的名称是个好主意吗?这将防止程序员意外地为模块(import urllib; urllib.os.getlogin())使用错误的名称,并避免“from x import *”名称空间污染为__all__。在

def _init_module():
   global foo
   import bar
   def foo():
       return bar.baz.operation()
   class Quux(bar.baz.Splort): pass
_init_module(); del _init_module

与使用__all__的相同模块相比:

^{pr2}$

函数可以采用这种样式来避免污染模块名称空间:

def foo():
    import bar
    bar.baz.operation()

对于一个希望帮助用户在交互式自省期间区分其API和包对其和其他模块API的使用的大型包来说,这可能很有帮助。另一方面,也许IPython应该在制表符完成期间简单地区分__all__中的名称,并且更多的用户应该使用一个IDE,允许他们在文件之间跳转以查看每个名称的定义。在


Tags: 模块用户import名称apifooinitdef
3条回答

from x import *的问题在于它可以隐藏NameErrors,这使得很难追踪到微小的bug。”名称空间污染“意味着在名称空间中添加一些你不知道它来自何处的东西。在

这也是你的结案方式。另外,它可能会混淆ide、outlines、pylint等。在

为模块使用“错误”的名称也不是真正的问题。无论从何处导入模块对象,它们都是相同的。如果“错误”的名字消失了(在更新之后),那就应该弄清楚原因,并激励程序员下次正确地这么做。但它不会引起虫子。在

我是一个写代码的粉丝,它绝对是头脑中最简单的。在

__all__是Python的一个特性,它显式添加是为了解决限制模块显示哪些名称的问题。当你使用它时,人们会立刻明白你在用它做什么。在

你的收尾技巧很不规范,如果我遇到了,我不会马上理解的。您需要输入一个长注释来解释它,然后需要再输入一个长注释来解释为什么要这样做,而不是使用__all__。在

编辑:既然我对这个问题有了更好的理解,这里有一个替代答案。在

在Python中,在模块中使用下划线作为私有名称前缀是一种很好的做法。如果您这样做from the_module_name import *,您将得到所有不以下划线开头的名称。因此,与其说闭包技巧,我更希望看到初始下划线习惯用法的正确使用。在

请注意,如果使用初始下划线名称,甚至不需要使用__all__。在

好吧,我开始对这个问题有点了解了。关闭确实允许隐藏私人物品。下面是一个简单的例子。在

没有关闭:

# module named "foo.py"
def _bar():
    return 5

def foo():
    return _bar() - 2 

关闭后:

^{pr2}$

使用示例:

>>> import foo
>>> dir(foo)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_bar', 'foo']
>>>
>>> import fooclosure
>>> dir(fooclosure)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'foo']
>>>

这实际上是令人不安的微妙之处。在第一种情况下,函数foo()只是引用了名称_bar(),如果要从名称空间中删除{},foo()将停止工作。foo()每次运行时都会查找_bar()。在

相反,foo()的闭包版本在名称空间中没有_bar()的情况下工作。我甚至不确定它是怎么工作的。。。它是保存对为_bar()创建的函数对象的引用,还是对仍然存在的名称空间的引用,以便它可以查找名称_bar()并找到它?在

相关问题 更多 >