我正在学习脚本语言python。我非常了解Java。我试图将一些代码从Java翻译成python。但他们的行为不规律(或者我的理解可能完全错误) 我在Java中有以下代码,我将无限期地向ArrayList添加元素。 所以这会导致outofmemory错误,我期待:
import java.util.*;
public class Testing{
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(4);
for (int i=0;i<list.size();i++){
list.add(5);
}
}
}
现在在python中翻译相同的代码:
lst = []
lst.append(5)
lst.append(4)
for i in range(len(lst)):
lst.append(5)
print lst
这里我得到输出:[5, 4, 5, 5]
从我看到的是,列表未作为对python中for
循环的引用传递?
同样在这里,
>>> l=[1,2,3]
>>> for i in l[:]:
... l.append(4)
... print l
...
[1, 2, 3, 4]
[1, 2, 3, 4, 4]
[1, 2, 3, 4, 4, 4]
在for
循环内的每次迭代中,我都在增加列表大小,所以迭代应该永远正确吗?
答案 0 :(得分:10)
python for
循环计算表达式,该表达式产生可迭代的循环一次。您可以在循环中操纵lst
对象,而不会影响for
循环的结果。这与Java for
构造(这是与Python for
语句非常不同的构造不同,后者实际上是Foreach构造),它为每次迭代计算3个关联表达式。
在第一个示例中,您创建了一个range()
结果,一旦创建了该结果,就不会为每个循环迭代更新它。
在第二个示例中,您使用全长切片(lst
)创建了lst[:]
的副本,以便循环迭代。不会为每个循环迭代重新创建副本。
然而,这里有一个警告。 for
循环在要迭代的对象上调用iter()
。对于列表对象,结果列表迭代器确实保留对原始列表的引用,以及迭代索引。每次for
循环前进迭代器(在其上调用next()
)时,迭代索引会递增并在原始列表中查找,直到索引等于列表长度。如果你继续添加循环中的列表,将创建一个无限循环。
如果您不创建要迭代的列表副本,您可以看到这一点:
>>> L = [1, 2, 3]
>>> for i in L:
... L.append(4)
... print L
... if len(L) > 30:
... break
...
[1, 2, 3, 4]
[1, 2, 3, 4, 4]
[1, 2, 3, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
这里,为L
创建的迭代器不断产生下一个元素,因为L
在循环中被扩展,如果我没有在循环中添加长度限制,这将永远持续下去。 / p>
答案 1 :(得分:6)
range(len(lst))
创建一次范围,然后迭代,而在每次迭代时评估java list.size()
答案 2 :(得分:2)
&GT;是没有作为python中的for循环的引用传递的列表?
所有对象都在Python中通过引用传递,而在Python 中,所有都是一个对象。 (Java原始值不是,但即使是普通的整数和浮点值也是Python中的对象。)
&GT;在for循环中的每次迭代中,我都在增加列表大小,所以迭代应该永远正确吗?
你正在增加l的大小,这是正确的,但l [:]只被评估一次并产生l的浅表副本。该副本在循环中不会更改,因此对循环内部l的更改不会更改循环变量将采用的值集。
在该循环中将l [:]改为l,然后你会看到很多输出。
答案 3 :(得分:1)
最简单的方法是将for
循环转换为等效的while
循环来解释这一点。
在Java中:
for (int i=0;i<list.size();i++){
list.add(5);
}
int i=0;
while (i<list.size()) {
list.add(5);
++i;
}
在Python伪代码中:
for i in range(len(lst)):
lst.append(5)
_r = range(len(lst))
_i = iter(r)
while _i isn't done:
next(_i)
lst.append(5)
在实践中,您实际上并不需要了解iter
*和next
如何工作,或者“_i未完成”部分如何工作的详细信息**;关键是for
循环创建一个迭代器,然后迭代它。在你的情况下,它在range
对象上创建一个迭代器(或者在Python 2.x中,list
函数返回range
)。
但即使不知道,你可以看到你的len(lst)
只在开始时被评估一次,以创建迭代器,而每次通过循环评估Java list.size()
等价物
如果你想要等同于Java风格的for
循环,你必须明确地编写while
循环。
* iter
在任何可迭代(列表,范围,甚至是另一个迭代器)上创建迭代器。迭代器有点像智能对象,它具有对可迭代的引用和其中的“当前位置”,尽管在封面下它们很少以这种方式实现。在迭代器上调用next
会有效地返回当前位置的值,并将迭代器推进到下一个(或者实际上实现了迭代器的等价物)。
**实际上实际发生的是try:
/ except StopIteration:
,因为在完成的迭代器上调用next
会引发StopIteration
。当然它是用C语言实现的(或Java或.NET或RPython,用于其他Python实现),而C实际上使用了一些特殊的魔术代码来循环迭代器,使其更快一点,但几乎没有人需要考虑那部分。