网站首页 > 技术文章 正文
【回复“python”,送你十本电子书】
本文首发于微信公众号号“编程派”。微信搜索“编程派”,获取更多Python编程一手教程及优质资源吧。
有同学曾在微信中问小编什么是非局部语句(nonlocal statement),本文就是对此的回答,希望没有发的太晚。
非局部语句是Python 3.x中新引入的特性。官方文档中的说法是,非局部语句可以让所列的标识符(identifier)指向最近的嵌套作用域(enclosing scope)中已经绑定过的变量,全局变量除外。
如果没有非局部语句
一般来说,嵌套函数对于其外层作用域中的变量是有访问权限的。
我们在outside函数中声明了msg变量,并赋值为“Outside!”。然后,在inside函数中打印msg的值。结果证明,inside成功获得了外层作用域中msg的值。
但是如果我们想给外层作用域中的变量赋值时,是不是按照平常的赋值操作就可以修改它的值呢?
>>> def outside: msg = "Outside!" def inside: msg = "Inside!" print(msg) inside
print(msg)
>>> outside
Inside! # inside函数打印的msg
Outside! # outside函数打印的msg
在inside函数中,我们想给msg变量赋值为"Inside!"。运行outside时,inside函数中msg的值为"Inside!",但是在outside函数中却保留了原先的值!
之所以出现这个情况,是因为在inside函数中,Python实际上并没有为之前已经创建的msg变量赋值,而是在inside函数的局部作用域(local scope)中创建了一个名叫msg的新变量,但是这样就和外层作用域(outer scope)中的变量重名了。
这说明,嵌套函数对外层作用域中的变量其实只有只读访问权限。如果我们在这个示例中的inside函数的顶部再加一个print(msg)语句,那么就会出现UnboundLocalError: local variable 'msg' referenced before assignment这个错误。
非局部语句的引入,就是要尽量减少这种变量名冲突情况的出现,同时也让嵌套函数更加方便的操作外层函数中的变量。更加详细的原因,请看参考资料部分的PEP-3104。
使用非局部语句之后
接下来,我们引入nonlocal语句。
>>> def outside: msg = "Outside!" def inside: nonlocal msg msg = "Inside!" print(msg) inside
print(msg)
>>> outsideInside! Inside!
现在,我们在inside函数的顶部添加了nonlocal msg语句。这个语句的作用,就是告诉Python解释器在碰到为msg赋值的语句时,应该向外层作用域的变量赋值,而不是声明一个重名的新变量。这样,两个函数的打印结果就一致了。
nonlocal的用法和global非常类似,只是前者针对的是外层函数作用域的变量,后者针对的则是全局作用域的变量。
什么时候该使用非局部语句
有时候,你可能会疑惑什么时候才应该使用nonlocal。以下面的函数为例:
>>> def outside:
d = {"outside": 1}
def inside:d["inside"] = 2 print(d) inside
print(d)
>>> outside{'inside': 2, 'outside': 1} {'inside': 2, 'outside': 1}
你可能会想,因为没有使用nonlocal,inside函数中往字典d中插入的"inside": 2键值对(key-value pair)不会体现在outside函数中。你这么想挺合理,但却是错的。因为字典插入并不是赋值操作,而是方法调用(method call)。事实上,往字典中插入一个键值对相当于调用字典对象中的__setitem__方法。
所以,这个示例中我们可以不使用nonlocal,就能直接操作外层作用域中的变量。
小结
其实在许多Python程序中,很少用到非局部语句。但是,有了这种语句之后,我们就可以减少不同作用域之间变量名的冲突。非局部语句,也让我们更加容易地访问、操作外层作用域中的变量。不过,这在一定程度上也让语法变得更加复杂。
有关变量、语句等术语的基础知识,还可以参考《Think Python 2e》的第二章:量、表达式和语句。
参考资料
猜你喜欢
- 2025-08-01 Visual C++对C/C++新标准的支持有何诟病之处?C语言有哪些标准?
 - 2025-08-01 《学习C++》基本概念之名称
 - 2025-08-01 《学习C++》之基本概念
 - 2025-08-01 仓颉编程语言基础-面向对象编程-接口(Interface)核心特性
 - 2025-08-01 《学习C++》基本概念之标识符
 - 2025-05-08 定义变量类型总是在变量前面吗?如何理解typedef定义的类型?
 - 2025-05-08 7. 包管理与模块(包和模块的区别)
 - 2025-05-08 苹果来真的!明年起不符合隐私要求的App将从应用商店移除
 - 2025-05-08 Java枚举(java枚举菜鸟教程)
 - 2025-05-08 可验证凭证(VC):数字时代的重要信任基石
 
- 最近发表
 - 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
 - [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
 - 超详细手把手搭建在ubuntu系统的FFmpeg环境
 - Nginx运维之路(Docker多段构建新版本并增加第三方模
 - 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
 - Go 人脸识别教程_piwigo人脸识别
 - 安卓手机安装Termux——搭建移动服务器
 - ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
 - Rust开发环境搭建指南:从安装到镜像配置的零坑实践
 - Windows系统安装VirtualBox构造本地Linux开发环境
 
 
- 标签列表
 - 
- cmd/c (90)
 - c++中::是什么意思 (84)
 - 标签用于 (71)
 - 主键只能有一个吗 (77)
 - c#console.writeline不显示 (95)
 - pythoncase语句 (88)
 - es6includes (74)
 - sqlset (76)
 - apt-getinstall-y (100)
 - node_modules怎么生成 (87)
 - chromepost (71)
 - flexdirection (73)
 - c++int转char (80)
 - mysqlany_value (79)
 - static函数和普通函数 (84)
 - el-date-picker开始日期早于结束日期 (76)
 - js判断是否是json字符串 (75)
 - c语言min函数头文件 (77)
 - asynccallback (87)
 - localstorage.removeitem (77)
 - vector线程安全吗 (73)
 - java (73)
 - js数组插入 (83)
 - mac安装java (72)
 - 无效的列索引 (74)
 
 
