我正在使用Dive Into Python 3本书学习Python。我喜欢它,但我不理解6.5节中的example used to introduce Closures。
我的意思是,我知道它是如何工作的,我觉得这很酷。但是我没有看到任何真正的好处:在我看来,通过在循环中逐行读取规则文件,并为每行读取进行搜索/替换,可以实现相同的结果。
有人可以帮助我:
要么理解为什么在这个例子中使用闭包会改进代码(例如,更容易维护,扩展,重用或调试?)
或建议一些其他真实代码示例的来源,其中闭包确实闪耀?
谢谢!
答案 0 :(得分:24)
装饰器是闭包的一个例子。例如,
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
对象引用的闭包。
最后,我还没有看过那本书,但是你快速阅读了你所关联的章节,我对此非常不满意 - 它是如此可怕的迂回曲折,以至于对闭包的解释毫无用处。
答案 1 :(得分:2)
当您可以访问整个代码库或者没有考虑可重用性时,这似乎不是特别有用,但是当尝试将逻辑分离成可以实现的不同可重用模块时,它非常强大且有用。由不同的开发人员并行。如果你只是简单地从文件中读取模式字符串,那么每个模块都必须知道这个文件并传递那个烦人的模式字符串列表。如果您更改系统以使模式字符串来自URL而不是来自文件,则可能会完全破坏整个代码库。另一方面,如果您处理逻辑只需要一个回调函数或几个回调函数,然后您有另一个模块使用文件中的内容动态构造函数,那么只有构造函数的组件需要更改。这是能够动态创建函数的力量。
答案 2 :(得分:2)
这是一个关闭用法,get configure:
def confmaker():
cf=ini_conf()
def clo(*args):
return cf.get(*args)
return clo
cfget=confmaker()
cfget(...)
这里ini_conf只被调用一次。在我的理解中,闭包避免了全局变量(比如cf),并使用法变得简单。
答案 3 :(得分:1)
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中创建了单独的命名函数,配对为匹配和应用,这是多余的。这些函数都基于相同的模式,从不直接调用。在这里,他修改了这段代码,以便更改规则。这变成了进一步的抽象级别,现在规则被指定为名为pattern的变量中的字符串。复数规则不再是函数。
6.5模式文件:由于没有更多重复的代码和字符串列表中定义的复数规则,构建生成器的下一步是将这些字符串放在一个单独的文件中。在这里,它们变得更易于维护,与使用它们的代码分开。6.6生成器:生成器是一个通用的plural()函数,它解析规则文件,检查匹配,适当时应用规则,然后转到下一个规则。这是一个闭包的例子。
这就是所有复数()函数必须要做的,而且所有复数()函数都应该这样做。
一个相对简单而美观的开发,非常复杂,非常有用,可以扩展到其他类型的问题,特别是在文本模式识别中。
作者在本教程结束时讨论了此特定解决方案的性能问题。从文件中打开和读取行会降低性能,尤其是越来越多的open()调用。他声称使用迭代器可以获得更好的性能,本书后面将对此进行讨论。
答案 4 :(得分:0)
在循环中逐行读取规则文件
循环中的逐行
环
这将推动整场比赛的表现。阅读一次,多次使用。