返回介绍

为什么使用 nonlocal

发布于 2024-01-29 22:24:16 字数 2570 浏览 0 评论 0 收藏 0

假设有了极其复杂的嵌套函数,你会为一团糟乱而感到吃惊。尽管很难在我们的小示例中看到这点,但在很多程序中,状态信息变得很重要。在Python中,有各种不同的方法来“记住”跨函数和方法的信息。尽管都有利有弊,对于嵌套的作用域引用,nonlocal确实起到了改进作用——nonlocal语句允许在内存中保持可变状态的多个副本,并且解决了在类无法保证的情况下的简单的状态保持。

正如我们在前面小节所看到的,如下的代码允许在一个嵌套作用域中保持和修改状态。对tester的每次调用都创建了可变信息的一个小小的自包含包,可变信息的名称不会与程序的其他部分产生任何冲突:

遗憾的是,这段代码只能在Python 3.0中工作。如果你使用Python 2.6,根据你的目标的不同,也有其他的选择。下面两个小节介绍了一些替代方法。

与全局共享状态

在Python 2.6中实现nonlocal效果的一种通常方法也是较早的方法,就是直接把状态移出全局作用域(嵌套的模块):

在这个例子中,这是有效的,但它需要在两个函数中都有global声明,并且倾向于引起全局作用域中的名称冲突(如果“状态”已经使用了会怎样?)。更糟糕但更为微妙的问题是,它只考虑到模块作用域中状态信息的单个共享副本——如果我们再次调用tester,将会重新设置模块的状态变量,以至于前面的调用将会看到自己的状态被覆盖:

正如前面所示,当使用nonlocal而不是global的时候,对tester的每次调用都记得state对象的自己的独特副本。

使用类的状态(预览)

Python 2.6中针对可改变信息的另一种较早的方法是使用带有属性的类,从而让状态信息的访问比隐式的范围查找规则更明确。作为一个额外的优点,一个类的每个实例都得到状态信息的一个新副本,作为Python的对象模型的一个天然的副产品。

我们还没有详细地介绍类,作为一个简短的概览,这里把前面使用的tester/nested函数作为类来重新实现——state在对象创建的时候显式地保存在对象中。为了让这段代码有意义,我们需要知道像这样的一个类中的def与一个类之外的def完全一样的工作,除非函数的self参数自动接收隐式的调用主体(通过调用类自身创建的一个实例对象):

我们将在本书后面更深入地介绍Python的神奇之处,我们也可以使用运算符重载让类看上去像是一个可调用函数。__call__获取了一个实例上的直接调用,因此,我们不需要调用一个指定的方法:

在本书中,此刻不要太多地探究这段代码的细节,我们将在第六部分深入探讨类,并且在第29章看到__call__这样的特定运算符重载工具,因此,你可能想要记下这段代码以供将来参考。这里的关键是类可以让状态信息更明显,通过利用显示属性赋值而不是作用域查找。

尽管使用类存储状态信息通常是可以遵从的良好规则,在这里所示的情况下,它们可能有些杀鸡用牛刀了,其中状态是一个单个的计数器。这样小的状态例子比你所想象的更常见,在这样的环境下,嵌套的def有时候比编写类还要简单,特别是,如果你还不熟悉OOP的话。此外,还有一些情况,嵌套的def可能实际上比类表现得更好(参见第38章中对方法装饰器的介绍,那里有一个示例,这一话题超出了本书的范围)。

使用函数属性的状态

作为最后一种状态保持选项,我们有时候可以使用函数属性实现与nonlocal相同的效果——用户定义的名称直接附加给函数。这里给出了基于这一技术的示例的最后一个版本——它用附加给嵌套的函数的一个属性替代了nonlocal。尽管这种方法可能对某些人来说不那么容易理解,但它允许从嵌套的函数之外访问状态变量(使用nonlocals,我们只能在嵌套的def内部看到状态变量):

这段代码依赖于一个事实:函数名nested是包围nested的tester作用域中的一个本地变量;同样,它可以在nested内自由地引用。这段代码还依赖于这样一个事实:本地修改一个对象并不是给一个名称赋值;当它自增nested.state,它是在修改对象nested引用的一部分,而不是指定nested本身。由于我们不是要真的在嵌套作用域内给一个名称赋值,所以不需要nonlocal。

正如你所看到的,全局、非本地、类和函数属性都提供了状态保持的选项。全局只支持共享的数据,类需要OOP的基本知识,类和函数属性都允许在嵌套函数自身之外访问状态。通常,你的程序的最好的工具取决于程序的目的。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文