代码之家  ›  专栏  ›  技术社区  ›  joeforker

python闭包是不是可以很好地替换“全部”的?

  •  3
  • joeforker  · 技术社区  · 15 年前

    用闭包代替 __all__ 限制python模块公开的名称?这将防止程序员意外地为模块使用错误的名称( import urllib; urllib.os.getlogin() )以及避免 from x import * “命名空间污染为 阿尔法 .

    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__ = ['foo']
    import bar
    def foo():
        return bar.baz.operation()
    class Quux(bar.baz.Splort): pass
    

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

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

    对于希望帮助用户在交互式内省期间区分其api与包对其和其他模块的api的使用的大型包来说,这可能是有帮助的。另一方面,也许ipython应该在 阿尔法 在制表符完成过程中,更多的用户应该使用一个IDE,允许他们在文件之间跳转以查看每个名称的定义。

    3 回复  |  直到 15 年前
        1
  •  7
  •   steveha    15 年前

    我是一个编写代码绝对是脑死亡的简单的风扇,因为它可以。

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

    你的闭包技巧非常不规范,如果我遇到它,我不会立即理解它。你需要输入一个长注释来解释它,然后你需要输入另一个长注释来解释为什么你这样做而不是使用 阿尔法 .

    编辑:现在我对这个问题有了更好的理解,下面是另一个答案。

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

    请注意,如果使用初始下划线名称,甚至不需要使用 阿尔法 .

        2
  •  4
  •   Jochen Ritzel    15 年前

    问题在于 from x import * 是它可以隐藏 NameErrors 这使得小虫子很难找到。”名称空间污染”意味着向名称空间中添加您不知道它来自何处的内容。

    这也是你的闭包的作用。另外,它可能会混淆ides、outlines、pylint等等。

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

        3
  •  1
  •   steveha    15 年前

    好吧,我开始更了解这个问题了。关闭确实允许隐藏私人信息。这里有一个简单的例子。

    没有结束:

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

    结束时:

    # module named "fooclosure.py"
    def _init_module():
        global foo
        def _bar():
            return 5
    
        def foo():
            return _bar() - 2
    
    _init_module(); del _init_module
    

    使用示例:

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

    这实际上是令人不安的微妙。在第一种情况下,函数 foo() 只是提到了这个名字 _bar() ,如果要移除 () 从名字空间来看, 英尺() 就会停止工作。 英尺() 仰视 () 每次它跑的时候。

    相反,闭包版本 英尺() 在没有 () 存在于名称空间中。我甚至不确定 怎样 它起作用了…它是否包含对为创建的函数对象的引用 () 或者,它是否持有对仍然存在的名称空间的引用,以便可以查找名称 () 找到它了吗?