为什么列表在python和Java中表现不同?

时间:2013-08-27 17:52:56

标签: java python arrays list for-loop

我正在学习脚本语言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循环内的每次迭代中,我都在增加列表大小,所以迭代应该永远正确吗?

4 个答案:

答案 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实际上使用了一些特殊的魔术代码来循环迭代器,使其更快一点,但几乎没有人需要考虑那部分。