【问题标题】:Better illustration of Closures?更好的闭包说明?
【发布时间】:2010-05-09 06:44:15
【问题描述】:

我正在使用Dive Into Python 3 书学习 Python。喜欢,不过6.5节的example used to introduce Closures看不懂。

我的意思是,我知道它是如何工作的,我认为它真的很酷。但我没有看到任何真正的好处:在我看来,只需在循环中逐行读取规则文件,并对读取的每一行进行搜索/替换,就可以获得相同的结果。

谁能帮我:

  • 要么理解为什么在此示例中使用闭包会改进代码(例如,更易于维护、扩展、重用或调试?)

  • 或者建议一些其他真实代码示例的来源,其中闭包真的很出色?

【问题讨论】:

    标签: python python-3.x closures


    【解决方案1】:

    装饰器是闭包的一个例子。例如,

    def decorate(f):
        def wrapped_function():
            print("Function is being called")
            f()
            print("Function call is finished")
        return wrapped_function
    
    @decorate
    def my_function():
        print("Hello world")
    
    my_function()
    

    函数wrapped_function 是一个闭包,因为它保留了对其作用域内变量的访问权——尤其是参数f,即原始函数。闭包允许您访问它。

    闭包还允许您在函数调用之间保留状态,而无需求助于类:

    def make_counter():
        next_value = 0
        def return_next_value():
            nonlocal next_value
            val = next_value
            next_value += 1
            return val
        return return_next_value
    
    my_first_counter = make_counter()
    my_second_counter = make_counter()
    print(my_first_counter())
    print(my_second_counter())
    print(my_first_counter())
    print(my_second_counter())
    print(my_first_counter())
    print(my_second_counter())
    

    此外,绑定方法在技术上是闭包(尽管它们的实现方式可能不同)。绑定方法是类成员函数,它们的类被烘焙:

    import sys
    w = sys.stdout.write
    w("Hello\n")
    

    w本质上是一个引用sys.stdout对象的闭包。

    最后,我还没有读过那本书,但快速阅读了你链接的章节,我很不喜欢——它是如此可怕的迂回,以至于它作为闭包的解释毫无用处。

    【讨论】:

    • 谢谢!我很想使用与此答案的风格和深度相似的 Python 3 书籍,但我找不到...
    【解决方案2】:

    当您可以访问整个代码库或不考虑可重用性时,这可能看起来不是特别有用,但是当您尝试将逻辑分离到可以实现的不同的、可重用的模块时,它非常强大和有用由不同的开发人员并行。如果你只是从文件中读取模式字符串,每个模块都必须知道这个文件并传递那个烦人的模式字符串列表。而且,如果您更改系统以使模式字符串来自 URL 而不是来自文件,它可能会完全破坏您的整个代码库。另一方面,如果您的处理逻辑只是采用一个回调函数或几个回调函数,然后您有另一个模块使用文件中的内容动态构造函数,那么只有构造函数的组件需要更改。这就是能够动态创建函数的力量。

    【讨论】:

    • 是的;但为什么不只传递字符串列表呢?一个模块将处理文件或 URL 或我们拥有的任何其他输入源。程序的其余部分会将实际规则作为模式字符串列表。
    • @user336527,其他模块可以简单地验证对象是否满足谓词列表。它甚至不需要知道那些谓词是什么。这简化了第二个模块的任务,并允许它与与字符串匹配无关的谓词重用。虽然,我同意,这个例子有点做作。它有比示例更好的用例。
    【解决方案3】:

    这里是 get 配置的闭包用法:

    def confmaker():
       cf=ini_conf()
       def clo(*args):
          return cf.get(*args)
       return clo
    
    cfget=confmaker()
    
    cfget(...)
    

    这里的ini_conf 只被调用一次。在我的理解中,闭包避免了全局变量(如 cf),并且使使用变得简单。

    【讨论】:

      【解决方案4】:

      Niels-Bom 写道(有编辑):

      只需在循环中逐行读取规则文件,并对读取的每一行进行搜索/替换,即可获得相同的结果。

      事实上,这基本上是在第 6.5 节中完成的,其中规则放置在文件plural4-rules.txt 中。现在规则作为字符串可以在文件中维护,我们的代码将数据与控制分开。这使项目更易于管理和维护。

      Niels 的问题促使我勾勒出第 6 章的发展,以便准确理解作者试图展示的内容。在所提供的开发中可以学到很多经验教训,这不仅仅是关于闭包,还有关于编码的最佳实践。

      该练习帮助我了解了如何使用生成器来替代替代的、不那么抽象且更复杂的实现。从 6.2 到 6.6 的材料发展具有足够的教育意义,可以在这里勾勒出来。

      所以我从 Niels 的第二点开始,即在 6.3 中将规则分解为单独的函数,作为草图的延续:

      为什么在这个例子中使用闭包可以改进代码?

      作者此时表示:

      添加这种抽象级别值得吗?嗯,还没有。

      仍有部分 6.4-6.6 需要完成。闭包的故事,在这种情况下,构建复数生成器是逐步实现的,从称为复数(名词)的模块中的硬编码规则开始。因此,从第一个相关部分开始,总结我们到本章结尾的方式,我们有以下内容。

      6.2 让我们使用正则表达式:作者借此机会通过在初始复数函数中硬编码的复数规则来加强和扩展我们对正则表达式的理解。

      6.3。函数列表:将复数函数中硬编码的规则抽象为几个独立的函数。这是下一节的“垫脚石”。但这也说明了match_sxz()和match_sxz的用法有一个重要的区别。

      6.4 模式列表:我们在 6.3 中创建了单独的命名函数,配对为 match 和 apply,这一事实是多余的。这些函数都基于相同的模式,从不直接调用。在这里,他修改了这段代码,以便更简单地更改规则。这成为更深层次的抽象,现在将规则指定为称为模式的变量中的字符串。复数规则不再是函数。

      6.5 模式文件:不再有重复的代码和在字符串列表中定义的复数规则,构建生成器的下一步是将这些字符串放在单独的文件中。在这里,它们变得更易于维护,与使用它们的代码分开。

      6.6 生成器:生成器是一个通用的复数() 函数,它解析规则文件、检查匹配、应用规则(如果合适)并转到下一个规则。这是一个闭包的例子。

      这就是plural()函数必须做的,也是plural()函数应该做的。

      一个相对简单而漂亮的开发,足够复杂以至于有用,并且可以扩展到人们可能会发现的其他类型的问题,尤其是在文本模式识别方面。

      作者在教程结尾处解决了此特定解决方案的性能问题。从文件中打开和读取行会降低性能,尤其是在 open() 调用数量增加的情况下。他指出,使用迭代器可以获得更好的性能,本书后面会提到。

      【讨论】:

        【解决方案5】:

        在循环中逐行读取规则文件 ​

        逐行循环

        循环

        这将推动整个楼层的表现。一次阅读,多次申请。

        【讨论】:

        • 抱歉,我说的不是很准确。当然,我将从文件中读取的模式字符串存储在一个列表中,然后总是遍历该列表。但是传递函数列表比传递模式字符串列表更好吗?
        猜你喜欢
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-16
        • 1970-01-01
        • 1970-01-01
        • 2014-09-07
        相关资源
        最近更新 更多