- 译者序
- 前言
- 第1章 问答环节
- 第2章 Python 如何运行程序
- 第3章 如何运行程序
- 第4章 介绍 Python 对象类型
- 第5章 数字
- 第6章 动态类型简介
- 第7章 字符串
- 第8章 列表与字典
- 第9章 元组、文件及其他
- 第10章 Python 语句简介
- 第11章 赋值、表达式和打印
- 第12章 if 测试和语法规则
- 第13章 while 和 for 循环
- 第14章 迭代器和解析,第一部分
- 第15章 文档
- 第16章 函数基础
- 第17章 作用域
- 第18章 参数
- 第19章 函数的高级话题
- 第20章 迭代和解析,第二部分
- 第21章 模块:宏伟蓝图
- 第22章 模块代码编写基础
- 第23章 模块包
- 第24章 高级模块话题
- 第25章 OOP:宏伟蓝图
- 第27章 更多实例
- 第28章 类代码编写细节
- 第29章 运算符重载
- 第30章 类的设计
- 第31章 类的高级主题
- 第32章 异常基础
- 第34章 异常对象
- 第35章 异常的设计
- 第36章 Unicode 和字节字符串
- 字符串基础知识
- Python 的字符串类型
- 文本和二进制文件
- Python 3.0 中的字符串应用
- 转换
- 编码 Unicode 字符串
- 编码非ASCII文本
- 编码和解码非ASCII文本
- 其他 Unicode 编码技术
- 转换编码
- 在 Python 2.6 中编码 Unicode 字符串
- 源文件字符集编码声明
- 使用 Python 3.0 Bytes 对象
- 序列操作
- 创建 bytes 对象的其他方式
- 混合字符串类型
- 使用 Python 3.0(和 Python 2.6)bytearray 对象
- 使用文本文件和二进制文件
- Python 3.0 中的文本和二进制模式
- 类型和内容错误匹配
- 使用 Unicode 文件
- 在 Python 3.0 中处理 BOM
- Python 2.6 中的 Unicode 文件
- Python 3.0 中其他字符串工具的变化
- Struct二进制数据模块
- pickle对象序列化模块
- XML解析工具
- 本章小结
- 本章习题
- 习题解答
- 第37章 管理属性
- 第38章 装饰器
- 第39章 元类
- 附录A 安装和配置
- 附录B 各部分练习题的解答
- 作者介绍
- 封面介绍
为什么使用 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论