谁能解释一下python里的python for循环 index

注意for循环中变量的作用域 - Python高级教程- Python进阶|Scrapy教程|Python高级|Python深入
官方微博:
注意for循环中变量的作用域
foreincollections:pass在for 循环里, 最后一个对象e一直存在在上下文中。就是在循环外面,接下来对e的引用仍然有效。这里有个问题容易
for&e&in&collections:
&&&&pass在for 循环里, 最后一个对象e一直存在在上下文中。就是在循环外面,接下来对e的引用仍然有效。这里有个问题容易被忽略,如果在循环之前已经有一个同名对象存在,这个对象是被覆盖的。如果在有代码感知的IDE中, IDE会提示变量是“被重新声明的”, 但运行时却不会出错。&for循环不是闭包,可以使用dis模块分解以下代码可以看到:x&=&5
for&x&in&range(10):
print&x将代码保存到test.py文件,运行python -m dis test.pyC:\Users\Patrick\Desktop&python -m dis test.py& 1 & & & & & 0 LOAD_CONST & & & & & & & 0 (5)& & & & & & & 3 STORE_NAME & & & & & & & 0 (x)& 3 & & & & & 6 SETUP_LOOP & & & & & & &20 (to 29)& & & & & & & 9 LOAD_NAME & & & & & & & &1 (range)& & & & & & &12 LOAD_CONST & & & & & & & 1 (10)& & & & & & &15 CALL_FUNCTION & & & & & &1& & & & & & &18 GET_ITER& & & & && & 19 FOR_ITER & & & & & & & & 6 (to 28)& & & & & & &22 STORE_NAME & & & & & & & 0 (x)& 4 & & & & &25 JUMP_ABSOLUTE & & & & & 19& & & & && & 28 POP_BLOCK& 6 & & && & 29 LOAD_NAME & & & & & & & &0 (x)& & & & & & &32 PRINT_ITEM& & & & & & &33 PRINT_NEWLINE& & & & & & &34 LOAD_CONST & & & & & & & 2 (None)& & & & & & &37 RETURN_VALUE在其他语言里,for循环的初始化变量对于上下文同样是可见的,比如java, 因为java是强类型的语言, 如果重新声明已存在的变量IDE会提示错误, 当然不同通过编译。通常在python编程中(可能是大多数的动态语言),有时即使声明了同名的变量,程序没有出现明显的错误,但是一旦出错,错误很难被发现。所以要避免与for循环中的变量重名。在使用python模板语言编码时尤其如此。代码编辑器没有提示,不会发现错误在哪里。这个是我碰到的极其怪异的一个例子。为什么说怪异,因为逻辑上没有任何问题。在一个页面模板里面,当handler调用这个模板时,同时传递了两个对象(从handler中,我使用tornado),一个page对象和一个pages列表。我的顺序是这样的:&!-- 用page对象 --&&label&{{ page.name if page else ''}}&/label&&!-- 用pages对象 --&&label&Parent Page& & &select name=&parent_id&&& & & & {% if pages %}& & & & & & {% for page in pages%}& & & & & & &option value=&{{ page.id}}&&{{page.name}}&/option&& & & & & & {% end %}& & & & {% end %}& & & & &option value=&&&None&/option&& & &/select&&/label&&!-- 然后又page --&&div&{{ page.markdown if page else ''}}&/div&问题来了,在运行的时候出错了,提示在 &label&{{ page.name if page else ''}}&/label& 中错误page referenced before assignment.晕死了, 找了一晚上的错,最后在把for循环中page的名字改为_page才运行了。在模板调用过程里,模板语言也是被翻译到python字节码,并按行解析和出,所以根本没有逻辑,不知道是tornado模板语言的bug。所以注意变量名。总之我认为tornado的exception trace非常不友好。Python中变量的作用域搜索顺序:本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)
相关文章:
相关词搜索:
如对本文内容有疑问,或想进一步交流学习,欢迎通过以下方式:
2. python技术互助群:
群④ : (已满)
3. 关注PythonTab微信,获取精彩资讯教程,公众号:Pythontab中文网
版权声明:本站文章除非注明,均为原创内容,如需转载请务必注明出处,违者本站保留追究其法律责任之权利。
频道总排行
频道本月排行当前访客身份:游客 [
当前位置:
可以写成这样的代码吗?
file=open('roemo.txt')
list_1=list()
for line in file:
line.rstrip()
#line.split()这样得到的line是一个整体(list的一个元素),把string转化为list
#list_1.append(line) 循环文件中的行数(4行)后,得到是4个元素
line.split()#返回的是一个字符串
list_1=list_1+line.split()
print list_1
print type(line.split())
#----------------------------------------------------------------------
以上是从文件里,把所有单词装到list里面,代码可以运行并且得到满意结果。
#下面是把list_1里面重复的元素给去掉
list_2=list()
#新建list_2空的
#遍历list_1中的每一个元素
for i in list_1:
for j in list_2:
#和list_2里的所有元素比较,如果不同,则填充到list_2里面。
#list_2不断增多。
list_2.append(i)
print list_2
问题来了:list_2的元素是空的!
list_2好像从被定义为空的以后,就没有增加过其中元素。
新人小白刚开始学python,请前辈纠正错误。
共有2个答案
<span class="a_vote_num" id="a_vote_num_
因为list_2是空的,所以第二层循环一直就没执行过。
你要获得不重复的list,就用:list_2 = list(set(list_1)) 好了
--- 共有 3 条评论 ---
: 可是我刚刚把list_2的初始值给换成' '(空格),代码执行下去,list_2得到的还是空的...能不能有什么方法,把这个list_2的for循环改一下?(我知道有更简单的去重方法,可是我很想搞懂这个list_2的for循环究竟怎么改才能生效)
(3个月前)&nbsp&
: 虽然Python里支持循序的过程中修改列表,但因为你这边list_2是空的,所以第二层循环的代码根本没机会去执行。
(3个月前)&nbsp&
在for循环里,for i in list_2中,list_2不随着循环里的list_2变化而变化吗?
(3个月前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
if i not in list_2:
& & list_2.append(i)
更多开发者职位上
有什么技术问题吗?
类似的话题Python的for循环可以循环所有序列(一个list或者string), 如:
a = ['abc', 'bcde', 'efghi', 'hello word']
for i in a:
print (len(i))
代码在Python3.0下调试通过
同类其他面试题 点击新一篇或旧一篇可浏览全部同类面试题
你有答案? 你对以上面试题有意见? 你想发表你的见解? 写下来吧!你的分享将会让很多人受益!
相关面试题
版权声明:本站大部分内容为原创! 另有少部分内容整理于网络,如需转载本站内容或关切版权事宜请联系站长。未经允许,严禁复制转载本站内容,否则将追究法律责任。
本站欢迎与同类网站建立友情链接,请联系QQ:页面导航:
→ 正文内容 Python中for循环下的索引变量的作用域
讲解Python中for循环下的索引变量的作用域
这篇文章主要介绍了讲解Python中for循环下的索引变量的作用域,是Python学习当中的基础知识,本文给出了Python3的示例帮助读者理解,需要的朋友可以参考下
我们从一个测试开始。下面这个函数的功能是什么?
def foo(lst):
for i in lst:
for t in lst:
return a, b
如果你觉得它的功能是“计算lst中所有元素的和与积”,不要沮丧。通常很难发现这里的错误。如果在大堆真实的代码中发现了这个错误就非常厉害了。――当你不知道这是一个测试时,很难发现这个错误。
这里的错误是在第二个循环体中使用了i而不是t。等下,这到底是怎么工作的?i在第一个循环外应该是不可见的&#63; [1]哦,不。事实上,Python正式声明过,为for循环目标(loop target)定义的名称(更严格的正式名称为“索引变量”)能泄露到外围函数范围。因此下面的代码:
for i in [1, 2, 3]:
这段代码是有效的,可以打印出3。在本文中,我想探讨一下为什么会这样,为什么它不太可能改变,以及将它作为一颗追踪子弹来挖掘CPython编辑器中一些有趣的部分。
顺便说一句,如果你不相信这种行为可能会导致真正的问题,考虑这个代码片断:
def foo():
for i in range(4):
lst.append(lambda: i)
print([f() for f in lst])
如果你期待上面的代码能打印出[0,1,2,3],你的期望会落空的,它会打印出[3,3,3,3];因为在foo的作用域内只有一个i,这个i就是所有的lambda所捕获的。
Python参考文档中的for循环部分明确地记录了这种行为:
&&& for循环将变量赋值到目标列表中。……当循环结束时,赋值列表中的变量不会被删除,但如果序列是空的,它们将不会被赋值给所有的循环。
注意最后一句,让我们试试:
for i in []:
的确,上面的代码抛出NameError异常。稍后,我们将看到这是Python虚拟机执行字节码方式的必然结果。
为什么会是这样
其实我问过Guido van Rossum有关这个执行行为的原因,他很慷慨地告诉了我其中的一些历史背景(感谢Guido!)。这样执行代码的动机是保持Python获得变量和作用域的简单性,而不诉诸于hacks(例如在循环完成后,删除定义在该循环中的所有变量――想想它可能引发的异常)或更复杂的作用域规则。
Python的作用域规则非常简单、优雅:模块、类以及函数的代码块可引入作用域。在函数体内,变量从它们定义到代码块结束(包括嵌套的代码块如嵌套函数)都是可见的。当然,对于局部变量、全局变量(以及其他nonlocal变量)其规则略有不同。不过,这和我们的讨论没有太多关系。
这里最重要的一点是:最内层的可能作用域是一个函数体。不是一个for循环体。不是一个with代码块。Python与其他编程语言不同(例如C及其后代语言),在函数水平下没有嵌套词法作用域。
因此,如果你只是基于Python实现,你的代码可能会以这样的执行行为结束。下面是另一段令人启发的代码片段:
for i in range(4):
变量d 在for循环结束后是可见及可访问的,你对这样的发现感到惊奇吗?不,这正是Python的工作方式。那么,为什么索引变量的作用域被区别对待呢?
顺便说一句,列表推导式(list comprehension)中的索引变量也泄露到其封闭作用域,或者更准确的说,在Python 3之前可以泄露。
Python 3包含许多重大更改,其中也修复了列表推导式中的变量泄露问题。毫无疑问,这样破坏了向后兼容中性。这就是我认为当前的执行行为不会被改变的原因。
此外,许多人仍然发现这是Python中的一个有用的功能。考虑一下下面的代码:
for i, item in enumerate(somegenerator()):
dostuffwith(i, item)
print('The loop executed {0} times!'.format(i+1))
如果不知道somegenerator返回项的数目,可以使用这种简洁的方式。否则,你就必须有一个独立的计数器。
这里有一个其他的例子:
for i in somegenerator():
if isinteresing(i):
dostuffwith(i)
这种模式可以有效的在循环中查找某一项并在随后使用该项。[2]
多年来,许多用户都想保留这种特性。但即使对于开发者认定的有害特性,也很难引入重大更改了。当许多人认为该特性很有用,而且在真实世界的代码中大量使用时,就更不会除去这项特性了。
Under the hood
现在是最有趣的部分。让我们来看看Python编译器和VM是如何协同工作,让这种代码执行行为成为可能的。在这种特殊的情况下,我认为呈现这些的最清晰方式是从字节码开始逆向分析。我希望通过这个例子来介绍如何挖掘Python内部[3]的信息(这是如此充满乐趣!)。
让我们来看本文开篇提出的函数的一部分:
def foo(lst):
for i in lst:
产生的字节码是:
0 LOAD_CONST
3 STORE_FAST
6 SETUP_LOOP
24 (to 33)
9 LOAD_FAST
12 GET_ITER
13 FOR_ITER
16 (to 32)
16 STORE_FAST
19 LOAD_FAST
22 LOAD_FAST
25 INPLACE_ADD
26 STORE_FAST
29 JUMP_ABSOLUTE
32 POP_BLOCK
33 LOAD_FAST
36 RETURN_VALUE
作为提示,LOAD_FAST和STORE_FAST是字节码(opcode),Python用它来访问只在函数中使用的变量。由于Python编译器知道(编译时)在每个函数中有多少个这样的静态变量,它们可以通过静态数组偏移量而不是一个哈希表进行访问,这使得访问速度更快(因而是_FAST后缀)。我有些离题了。这里真正重要的是变量a和i被平等对待。它们都通过LOAD_FAST获取,并通过STORE_FAST修改。绝对没有任何理由认为它们的可见性是不同的。[4]
那么,这种执行现象是怎么发生的?为什么编译器认为变量i只是foo中的一个局部变量。这个逻辑在符号表中的代码中,当编译器执行到AST开始创建一个控制流图,随后会产生字节码。这个过程的更多细节在我有关符号表的文章中的介绍――所以我只在这里提及其中的重点。
符号表代码并不认为for语句很特别。在symtable_visit_stmt中有如下代码:
case For_kind:
VISIT(st, expr, s-&v.For.target);
VISIT(st, expr, s-&v.For.iter);
VISIT_SEQ(st, stmt, s-&v.For.body);
if (s-&v.For.orelse)
VISIT_SEQ(st, stmt, s-&v.For.orelse);
索引变量如任何其他表达式一样被访问。由于该代码访问了AST,这值得去看看for语句结点内部是怎样的:
For(target=Name(id='i', ctx=Store()),
iter=Name(id='lst', ctx=Load()),
body=[AugAssign(target=Name(id='a', ctx=Store()),
value=Name(id='i', ctx=Load()))],
orelse=[])
所以i在一个名为Name的节点中。这些是由符号表代码通过symtable_visit_expr中以下语句来处理的:
case Name_kind:
if (!symtable_add_def(st, e-&v.Name.id,
e-&v.Name.ctx == Load &#63; USE : DEF_LOCAL))
VISIT_QUIT(st, 0);
由于变量i被清楚地标记为DEF_LOCAL(因为* _FAST字节码是可访问的,但是这也很容易观察到,如果符号表是不能用的则使用symtable模块),上述明显的代码调用symtable_add_def与DEF_LOCAL 作为第三个参数。现在来浏览一下上面的AST,并注意到Name结点中i的ctx=Store部分。因此,它是在For结点的target部分存储着i的信息的AST。让我们看看这是如何实现的。
编译器中的AST构建部分越过了解析树(这是源代码中相当底层的表示――一些背景资料可以在这里获得),同时在其他事项中,在某些结点设置expr_context属性,其中最显著的是Name结点。想想看,这样一来,在下面的语句:
foo = bar + 1
for和bar这两个变量都将在Name结点中结束。但是bar只是被加载到这段代码中,而for实际上被存储到这段代码中。expr_context属性通过符号表代码被用来区分当前和未来使用[5] 。
回到我们for循环的索引变量。这些内容将在函数ast_for_for_stmt――for语句创建AST――中处理。下面是该函数的相关部分:
static stmt_ty
ast_for_for_stmt(struct compiling *c, const node *n)
asdl_seq *_target, *seq = NULL, *suite_
expr_ty target,
node_target = CHILD(n, 1);
_target = ast_for_exprlist(c, node_target, Store);
if (!_target)
return NULL;
/* Check the # of children rather than the length of _target, since
for x, in ... has 1 element in _target, but still requires a Tuple. */
first = (expr_ty)asdl_seq_GET(_target, 0);
if (NCH(node_target) == 1)
target = Tuple(_target, Store, first-&lineno, first-&col_offset, c-&c_arena);
return For(target, expression, suite_seq, seq, LINENO(n), n-&n_col_offset,
c-&c_arena);
在调用函数ast_for_exprlist时创建了Store上下文,该函数为索引变量创建了一个结点(注意,for循环的索引变量还可能是一序列变量的元组,而不仅仅是一个变量)。
在介绍为什么for循环变量和循环中的其他变量一视同仁的过程中,这个函数是最后总要的一部分。在AST中进行标记之后,在符号表和虚拟机中用于处理循环变量的代码与处理其他变量的代码是相同的。
本文讨论了Python中可能被认为是“疑难杂症”的某些特定行为。我希望这篇文章确实解释了Python的变量和作用域的代码执行行为,说明了为什么这些行为是有用的而且永远不太可能改变,以及Python编译器的内部如何使其正常工作。感谢您的阅读!
[1] 在这里,我很想开个Microsoft Visual C ++ 6的玩笑,但事实让人有些不安,因为在2015年这个博客的大部分读者不会懂这个笑话(这反映了我的年龄,而不是我的读者的能力)。
[2] 你可能会说,在执行到break之前时,dowithstuff(i)可以进入if中。但是,这并不总是很方便。此外,根据Guido的解释,这里对我们关注的问题做了一个很好的分离――循环被用于并只用于搜索。在搜索结束后,循环中的变量会发生什么已经不是循环关注的事情。我觉得这是非常好的一点。
[3]: 通常我的文章中的代码是基于Python 3。具体而言,我期待Python库中将要完成的下一个版本(3.5)的default分支。但是对于这个特定的主题,在3.x系列中的任何版本的源代码都应该是可以工作的。
[4] 函数分解中另一件很明显的事是,如果循环不执行,为什么i仍然是不可见的,GET_ITER和FOR_ITER这对字节码将我们的循环当做一个迭代器,然后调用其__next__方法。如果这个调用最后以抛出StopIteration异常结束,虚拟机捕捉到这个异常然后结束循环。只有实际值被返回,虚拟机才会继续对i执行STORE_FAST,因此让这个值存在,让后续代码可以引用。
[5] 这是一个奇怪的设计,我怀疑这个设计的实质是为了使用相对干净的递归访问AST中的代码,如符号表代码和CFG生成器。
您可能感兴趣的文章:
上一篇:下一篇:
最 近 更 新
热 点 排 行
12345678910Python循环语句中whlie和for的应用
阅读:2159次&&&时间: 19:20:26&&
Python循环语句是很多程序员必须掌握的一项技能,虽然有点老掉牙的话题,但是还需要大家不断的进行巩固。dict用一种更优雅的方式模拟了switch选择,集合lambda函数,还可以进一步实现更加复杂的逻辑分支语句。关于lambda函数的使用,我们到下一章再学习。
Python循环语句
Python循环语句支持两种循环语句&&while循环和for循环,不支持C#中的do-while循环。Python的while循环和C#基本一致,此处我们着重比较两种语言中for循环的区别。
说的简单一点,Python循环语句中的for语句相当于C#中的foreach语句,它常用于从集合对象(list、str、tuple等)中遍历数据。例如:
for&i&in&[1,2,3,4,5]: &print&i&
这与C#中的foreach语法基本是一样的,下边是C#中的对应代码:
IEnumerable&numbers&=&Enumerable.Range(0,&5); &foreach(&int&i&in&numbers) &Console.WriteLine(i); &
如何实现类似C#中for(int i = 0; i & 10; i++)这种for循环呢?答案是使用range或xrange对象,见下边的代码:
#&range(10)也可以用xrange(10)代替 &for&i&in&range(10): &print&i#等价于以下C#语句 &#for(int&i&=&0;&i;i++) &#&Console.WriteLine(i);&
内建函数range([i,]j[,stride])建立一个整数列表,列表内容为k(i &= k & j)。第一个参数i和第三个参数stride是可选的,默认值分别为 0 和 1。内建函数xrange([i,]j[,stride])与 range 有相似之处,但xrange返回的是一个不可改变的XRangeType对象。这是一个迭代器,也就是只有用到那个数时才临时通过计算提供值。当 j 值很大时,xrange能更有效地利用内存。
Python循环语句中的while和for循环中支持break和continue语句。break语句用于立刻中止循环,continue语句用于直接进入下一次循环(忽略当前循环的剩余语句)。break和continue语句在C#与Python中的用法是一致的,只用于语句所在的当前循环。如果需要退出一个多重循环,应该使用异常,因为Python中没有提供goto语句。
最后,Python中的循环还支持else语句,它只在循环正常完成后运行(for和while循环),或者在循环条件不成立时立即运行(仅while循环),或者迭代序列为空时立即执行(仅for循环)。如果Python循环语句使用break语句退出的话,else语句将被忽略。
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
Copyright &
All Rights Reserved

我要回帖

更多关于 python跳出for循环 的文章

 

随机推荐