python2 nonlocal_python unboundlocalerror

python2 nonlocal_python unboundlocalerror在廖雪峰的官网上看到一个很有意思题目。关于闭包的,有兴趣的朋友可以看一下这里,做一下这个题目,当然需要一点闭包的知识。下面我简述一下:利用闭包返回一个计数器函数,每次调用它返回递增整数。#修改下面这个函数defcreateCounter():defcounter():passreturncounter#测试:counterA=createCounter()print(counter…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

在廖雪峰的官网上看到一个很有意思题目。关于闭包的,有兴趣的朋友可以看一下这里, 做一下这个题目,当然需要一点闭包的知识。下面我简述一下:

利用闭包返回一个计数器函数,每次调用它返回递增整数。

# 修改下面这个函数

def createCounter():

def counter():

pass

return counter

# 测试:

counterA = createCounter()

print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5

counterB = createCounter()

if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:

print(‘测试通过!’)

else:

print(‘测试失败!’)

方法一

说实话这题对我来说还是有点难度的,但我尝试了几次之后也找到一个比较track的方法。一开始我是这么写的。

def createCounter():

i = 0

def counter(i=i):

i = i+1

return i

return counter

# 执行结果是: 1 1 1 1 1

这样当然是错的, 因为整数 是 不可变对象,当你作为参数传进去时都会创建一个新的内存空间,这里边其实还有很多学问,不是很了解的可以看一下stackoverflow上的这个回答。虽然失败了,但也让我想到一个track的方法,就是把i换成可变对象

def createCounter():

i = [0]

def counter():

i[0] = i[0]+1

return i[0]

return counter

# 执行结果是: 1 2 3 4 5

OK, 这样就没有问题了。但这并不是一个好的解决方法, 利用可变对象的这个特性有可能会引起变量作用域混乱的。于是我又想到了另一种解决。

方法二

另一种方法就是使用generator,在createCounter函数下创建一个从1开始的整数generator, 然后在cuonter函数中调用。由于generator保存的是算法,当调用next函数时就可以计算出下一个的值,直到没有元素报错。当然这里不用担心,generator可以创建无限集合。

def createCounter():

def inter():

n = 1

while True:

yield n

n = n+1

f = inter()

def counter():

return next(f)

return counter

上面的代码中,inter()就是一个包含从1开始的所有整数的generator。然后在counter里边调用。每次计算下一个的值。这样就可以实现计数的功能。说到generator,stackoverflow上有一个回答值得一读,即使你已经掌握这个也可以读一下,这个回答应该还是python问答当中排名第一的。链接在这里。

方法三

emmmm,想到这两种方法已经是极限了,于是我往评论区翻了翻,看一下大佬们有什么做法。然后就看到一个我没见过的关键字…其中有一个大佬是这么做

def creat_counter():

i=0

def counter():

nonlocal i

i=i+1

return i

return counter

学了python这么久,第一次看到nonlocal这个关键字,果然我还是太菜了。。。

不过从语句上看nonlocal的作用应该是把i变成全局变量,这样每次修改都可以生效,跟global关键字有点像。既然找到一个知识盲点,那就将它彻底解决吧。

nonlocal 与 global

说了这么多,是时候回到主题了,nonlocal关键字到底是什么?在什么情况下用呢?简单来说,nonlocal关键字是用来改变变量的作用域的,直接解释不太好懂。先来看两个例子吧。

def outside():

msg = “Outside!”

def inside():

msg = “Inside!”

print(msg)

inside()

print(msg)

执行结果是什么呢?

Inside!

Outside!

结果应该很好理解, 在outside函数里面定义了inside函数并且执行。当运行outside函数时,inside里面的msg变量指向了”Inside!”,outside里面的msg指向了”Outside!”, 也就是说这里其实有两个msg变量,并且指向了不同的值。如下图所示:

python2 nonlocal_python unboundlocalerror

变量示意图

再来看下面这个例子:

def outside():

msg = “Outside!”

def inside():

nonlocal msg

msg = “Inside!”

print(msg)

inside()

print(msg)

现在的执行结果就变成了:

Inside!

Inside!

两段代码之间的差别仅在于下面的例子多了一句 nonlocal msg。这里的nonlocal关键字起到了什么作用呢?nonlocal意思是告诉python,不要重新创建msg变量,而是使用outside中的msg变量来赋值。画个图就很好懂了。

python2 nonlocal_python unboundlocalerror

变量示意图

在这个例子中, msg变量只被创建了一次,首先将”Outside!”赋值给msg,然后将”Inside!”赋值给了msg, 此时的msg已经指向了”Inside!”。因此执行的结果两个都是”Inside!”。

现在我们知道了,nonlocal是用来改变变量的作用域的。本例中,nonlocal将inside函数里面的msg变量的作用域变成了outside块中的区域。nonlocal跟global 这两个关键字非常像,不同之处在于nonlocal用于外部函数作用域的变量,而global用于全局范围内的变量。

这就是nonlocal关键字的作用。但是还有一点值得注意,先看下面的例子。

def outside():

d = {“outside”: 1}

def inside():

d[“inside”] = 2

print(d)

inside()

print(d)

大家觉得输出是什么呢?

实际输出是这样的:

{‘outside’: 1, ‘inside’: 2}

{‘outside’: 1, ‘inside’: 2}

原因嘛,当然是因为dict是可变对象了,但由于 d[“inside”] = 2 这样的语句是有点让人迷惑的,看起来很像重新赋值。实际上dict的赋值是调用了setitem方法。这样看就不会感到迷惑了。

# 下面的代码是等价的。

d[“inside”] = 2

d.__setitem__(“inside”, 2)

关于nonlocal关键字,应该讲清楚了吧。至于python的闭包,其实还是挺复杂的,上面的只是几个例子,想要更加深入的学习可以上stackoverflow上看看大佬们的回答。很有学习的价值。

写文不易, 还请大家多多支持!

参考资料:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/196716.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 计算机适配器有什么作用,例举适配器是什么

    计算机适配器有什么作用,例举适配器是什么随着科技进步,网络的进步,电脑已逐渐渗透到我们生活的方方面面,但是我们对于电脑的一些配置却不怎么了解,比如我们经常用到的适配器。下面,我就将适配器的一些小知识分享给大家我们在生活中或者使用电脑的时候经常会看到适配器一词,很多朋友就纳闷了,适配器是什么呢?有什么作用呢?针对这些问题,小编给大家整理了一些适配器的介绍,赶紧来瞧瞧吧适配器介绍电脑图解1适配器是一个接口转换器,也就是一种起中间连接作用的配…

  • linux下svn命令使用大全

    最近经常使用svn进行代码管理,这些命令老是记不住,得经常上网查,终于找了一个linux下svn命令使用大全:1、将文件checkout到本地目录 svncheckoutpath(path是服务器 上的目录)例如:svncheckoutsvn://192.168.1.1/pro/domain简写:svnco2、往版本库中添加新的文件 svnaddfile例如:svn…

  • 浅析人脸检测之Haar分类器方法:Haar特征、积分图、 AdaBoost 、级联

    浅析人脸检测之Haar分类器方法:Haar特征、积分图、 AdaBoost 、级联浅析人脸检测之Haar分类器方法一、Haar分类器的前世今生      人脸检测属于计算机视觉的范畴,早期人们的主要研究方向是人脸识别,即根据人脸来识别人物的身份,后来在复杂背景下的人脸检测需求越来越大,人脸检测也逐渐作为一个单独的研究方向发展起来。      目前的人脸检测方法主要有两大类:基于知识和基于统计。Ø 基于知识的方法:主要利用先验知识将人脸看作器官特征的组合,根

  • 手机实名制果真成了空头口号

    手机实名制果真成了空头口号

  • Apache中 RewriteRule 规则参数介绍

    Apache中 RewriteRule 规则参数介绍Apache中RewriteRule规则参数介绍 摘要: Apache模块mod_rewrite提供了一个基于正则表达式分析器的重写引擎来实时重写URL请求。它支持每个完整规则可以拥有不限数量的子规则以及附加条件规则的灵活而且强大的URL操作机制。这里着重介绍RewriteRule规则以及参数说明。Apache模块mod_rewrite提供了一个基于正则表达式分析器的重写引擎来实…

  • JS中的位置和宽度:clientWidth、offsetWidth、scrollWidth等区别「建议收藏」

    JS中的位置和宽度:clientWidth、offsetWidth、scrollWidth等区别「建议收藏」首先定义一个div。然后稍微装修一下下面开始区分一、clientWidth和clientHeigh、clientTop和clientLeft1,clientWidth的实际宽度clientWidth=width+左右padding2,clientHeigh的实际高度clien…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号