python choose语句作用_理解闭包是如何与变量作用域相互影响的

原文标题:KNOW HOW CLOSURES INTERACT WITH VARIABLE SCOPE

比如说你现在想要对一组数字进行排序,同时希望提高一组数字的优先级使这组数字优先显示。这种模式在展示用户接口时非常有用,在展示用户接口时经常需要优先展示一些重要信息以及异常事件。

解决这类问题的一个常用方法是在调用排序方法时传递一个帮助方法作为关键字参数。帮助方法的返回值用于对列表中的每一个元素进行排序。帮助方法能够检查列表中的值是否属于优先显示数组。帮助方法的实例代码如下:

4801f32a7a21bf95b58c24b0eaa25423.png

下面是一个简单应用:

1a11c1f7812f276f3f4c80b5a3d3ee90.png

这个帮助方法能够正常工作有3个原因:

  • Python支持闭包:方法可以引用包含它们的作用域内的变量(functions that refer to variables from the scope in which they were defined)。这就是为什么helper方法可以访问group参数。
  • 在Python中方法是第一类对象(first-class object), 这意味着你可以直接引用方法、将方法赋值给变量、作为参数将方法传递给另一个方法、在if语句或者表达式用比较方法。这就是为什么sort方法可以接收另一个方法作为参数。
  • Python针对元组有一套特殊的比较规则。Python中元组的比较是按照元组中每个元素进行比较的,首先比较第0个元素,然后比较第一个,然后第二个,一直比较下去。这就是为什么在helper方法中返回两个不同的元组。

如果帮助方法在返回以上元组之外还能返回元素是否出现在了优先数组中将会对调用者更有帮助。看起来在前面代码的基础上实现这个行为非常容易,只需要在将代码做如下调整即可:

083c50def353550b8d876e8b42fc3128.png

执行代码:

565afdb637a080162818894e1172ab7d.png

排序正确但是found结果错误。这是为什么呢?

当你在一个表达式中引用变量时,Python解释器会按照以下顺序遍历所有作用域来找到参数引用:

  • 当前方法的作用域
  • 任何闭合作用域(例如其它外围方法)
  • 包含当前代码的模块的作用域(也叫 global作用域)
  • 内嵌作用域(例如len和str)

如果遍历完上面全部作用域还没有找到需要引用的名字,就会抛出一个NameError。

但是给变量赋值的顺序就不一样了。如果在当前作用域中已经定义了变量,那么这个变量就会直接获得新值。如果当前作用域中没有这个变量,那么Python会把赋值语句当作变量定义。这个新变量的作用域就是包含赋值语句的方法。

赋值语句的行为就解释了为什么sort_priority2 的返回值错误了。found变量在helper中被赋予了true,闭包中的赋值语句被认为是一个新变量,这不影响外面方法中的变量。

两个found的作用域:

57021037dd05bd33984629daeb75147e.png

这就是闭包bug。但是这又是希望的结果。这种行为可以有效的防止方法中的变量污染外部模块。否则方法内部的任意赋值语句都会影响全局变量。这样会产生很多噪音,还会引起奇怪的bug。

使数据可以被访问

Python 3:在Python 3中有一种特殊语法可以使闭包内的数据被提取出来。使用nonlocal语句可以指定一个变量的遍历方式是从上级开始遍历。唯一的限制就是nonlocal不能遍历到模块级别(防止污染全局变量)。

下面使用nonlocal语句重写上面的方法:

0b4a6f92017a741a8cfeec38e550d777.png

在此,nonlocal语句可以很清晰的将一个作用域的变量以入到另一个作用域。它是对global语句的一个补充,global指定全局变量。

然而,就像全局变量的反面模式一样(much like the anti-pattern of global variables),除了简单的函数,我不建议大家使用nonlocal。nonlocal的副作用很难被追踪。尤其是在一个非常长的方法中,nonlocal语句、赋值语句与相关的变量离的非常远的情况。

一旦nonlocal是的代码变复杂,你就需要考虑是否可以将代码打包成一个帮助方法。下面我修改前面的代码,不使用nonlocal以达到同样目的,代码有点长,但是更易于阅读:

b6eb91c95ef15ed2249c2e5187dc1ae6.png

Python 2:不幸的是,Python 2中根本就没有nonlocal关键字。为了达到同样目的,你需要利用Python的高级用法来实现一个变通方法。下面的代码实现并不友好,但却是Python中的常用方法。

967ea5d7469a39c3a682a26a8a7c9f05.png

就像前面解释的一样,Python会向上遍历found被引用的域以确定found当前的值。这里引起欺诈性就是found是一个列表,它的值非常易变。这意味着,一旦found被检索到,闭包方法就可以修改found的状态进而把值从内部作用域中传递出来(在这里使用 found[0] = True)。

除了使用列表,通过使用字典、集合、类的实例一样可以在域间传值。

注意事项:

  • 闭包方法可以在它们定义的作用域内引用任何变量;
  • 默认情况下,闭包方法不能给外部变量赋值;
  • 在Python 3中可以使用nonlocal语句指定修改闭包以外的变量;
  • 在Python 2中使用可变值变量来代替nonlocal语句;
  • 除了非常简单的方法外,避免使用nonlocal语句。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/420193.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习(1368):app.use使用

const express require(express);const app express(); app.use((req, res, next) > {console.log(请求走了use中间件);next(); }) app.use(/request, (req, res, next) > {console.log(请求走了use中间件/hh)next() }) app.get(/list, (req, res) > {//send 响应内…

JDE Client开发端 左侧边栏设置

转载于:https://www.cnblogs.com/GYoungBean/p/4299317.html

insert事务隔离mysql_MySQL数据库详解(三)MySQL的事务隔离剖析

提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务。最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱。转账过程具体到程序里会有一系列的操作,比如查询余额…

前端学习(1369):中间件应用

const express require(express);const app express(); app.use((req, res, next) {res.send(网站维护中); }) app.use(/admin, (req, res, next) > {let isLogin false;if (isLogin) {next()} else {res.send(你还没有登录);} }) app.get(/admin, (req, res) > {res.…

前端学习(1370):错误处理中间件

const express require(express);const app express(); app.get(/index, (req, res) > {throw new Error(程序发生了错误);/* res.send(); */ }) app.use((err, req, res, next) > {res.status(500).send(err.message); }) app.listen(3000); console.log(服务器启动成…

前端学习(1372):构建模块化路由

const express require(express);const app express(); //创建路由对象 const home express.Router(); app.use(/home, home); home.get(/index, (req, res) > {res.send(欢迎来到我的页面) }) app.listen(3000); console.log(服务器启动成功); 运行结果

wamp环境搭建到mysql就不成功_Wamp环境搭建常见错误问题解决

第一点、对于apache php mysql 的版本的正确选择问题:网上有些教学视频已经很早了,然后很多人照着来,完全和视频里讲的一样,但是结果就是搭建不成功。出现问题原因:三件套的版本选择不正确,比如有的php版…

前端学习(1373):构建模块化路由2

demo37.js const express require(express);const app express(); const home require(./home); const admin require(./admin);app.use(/home, home); app.use(/admin, admin);app.listen(3000); console.log(服务器启动成功); home.js const express require(express…

前端学习(1374):express参数中get参数的获取

const express require(express);const app express(); app.get(index, (req, res) > {res,end(req.query); })app.listen(3000); console.log(服务器启动成功); 运行结果

前端学习(1375):express参数中post参数的获取

demo39.js const express require(express);const app express(); const bodyParser require(body-parser); //拦截所有请求 //extends:true 方法内部使用第三方模块请求的参数 app.use(bodyParser.urlencoded({ extends: false }))app.post(/add, (req, res) > {res.se…

前端学习(1376):app.use方法

const express require(express);const app express(); const bodyParser require(body-parser); //拦截所有请求 //extends:true 方法内部使用第三方模块请求的参数 app.use(fn({ a: 1 }));function fn(obj) {return function(req, res, next) {if (obj.a 1) {console.log…

websocket + node.js聊天系统

转:http://www.cnblogs.com/Wayou/p/hichat_built_with_nodejs_socket.html 前端一直是一块充满惊喜的土地,不仅是那些富有创造性的页面,还有那些惊赞的效果及不断推出的新技术。像node.js这样的后端开拓者直接将前端人员的能力扩大到了后端。…

前端学习(1377):express路由参数

const express require(express);const app express(); const bodyParser require(body-parser); //拦截所有请求 //extends:true 方法内部使用第三方模块请求的参数app.get(/index/:id, (req, res) > {res.send(req.params); }) app.listen(3000); console.log(服务器启…

前端学习(1378):express静态资源处理

const express require(express); const pathrequire(path); const app express();app.use(express.static(path.join(__dirname))) app.listen(3000); console.log(服务器启动成功);

json字符串生成C#实体类的工具

转载:http://www.cnblogs.com/finesite/archive/2011/07/31/2122984.html json作为互联网上轻量便捷的数据传输格式,越来越受到重视。但在服务器端编程过程中,我们常常希望能通过智能提示来提高编码效率。JSON C# Class Generator 能将json格式所表示的J…

前端学习(1382):多人管理项目2案例初始化

blog.js const express require(express);const app express();const home require(./homegeyao); const admin require(./admingeyao);app.use(/home, home); app.use(/admin, admin); app.listen(3000);console.log(服务器启动成功); admingeyao.js //管理页面 //展示…

codeforces C. Xor-tree

http://codeforces.com/problemset/problem/430/C 题意:在一棵上有n个节点,有n-1条边,在每一个节点上有一个值0或1,然后给你一个目标树,让你选择节点,然后把节点的值翻转,它的孙子节点跟着翻转&…

前端学习(1383):多人管理项目3

blog.js const express require(express); //创建网站服务器 const app express(); //开放静态资源文件 const path require(path);//告诉express框架模板所在的位置 app.set(views, path.join(__dirname, views)); //告诉express框架模板的后缀是什么 app.set(view engine…

java 运算优先级_Java-运算符优先级

我们先后学习了不同的运算符,通过运算符我们的能进行各种不同的操作实现自己的想要的效果,但是此时还存在一个问题,当在一个表达式中,有可能包含多个有不同运算符连接起来的、具有不同数据类型的数据对象。由于表达式有多种运算&a…