1

我正在学习脚本语言python。我对Java相当了解。我正在尝试将一点代码从 Java 转换为 python。但它们的行为不规律(或者我的理解可能完全错误)我在 Java 中有以下代码,我在其中无限期地向 ArrayList 添加元素。所以这会导致内存不足错误,我期望:

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]

从我所看到的,列表是否没有作为对forpython中循环的引用传递?

同样在这里,

>>> 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

4 回答 4

10

python循环计算表达式,该表达式产生只循环一次for的可迭代对象。您可以在循环中操作对象,而不会影响循环的结果。这不同于 Java构造(它与 Python语句的构造非常不同,它实际上是一个Foreach构造),它为每次迭代计算 3 个关联的表达式。lstforforfor

在您的第一个示例中,您创建了一个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]

在这里,为 for 创建的迭代器L不断产生循环中扩展的下一个元素L,如果我没有在循环中添加长度限制,这将永远持续下去。

于 2013-08-27T17:56:38.070 回答
6

range(len(lst))创建一次范围,然后对其进行迭代,而在 javalist.size()中在每次迭代时进行评估

于 2013-08-27T17:54:47.020 回答
2

> is the list not passed as reference to the for-loop in python?

All objects are passed by reference in Python, and in Python everything is an object. (Java primitive values are not, but even plain integer and float values are objects in Python.)

> in each iteration inside for-loop, I am increasing the list size, so the iteration should go forever correct?

You are increasing the size of l, that's correct, but l[:] is evaluated just once and produces a shallow copy of l. That copy isn't changed in the loop, so changes to l inside the loop do not change the set of values that the loop variable will take on.

Change l[:] to just l in that loop, and then you'll see a lot of output.

于 2013-08-27T18:05:22.153 回答
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 中,listrange函数返回)。

但即使不知道这一点,您也可以看到您len(lst)只在开始时被评估一次,以创建迭代器,而 Javalist.size()等价物每次通过循环都被评估。


If you want the equivalent of a Java-style for loop, you have to write the while loop explicitly.


* iter creates an iterator over any iterable (a list, a range, even another iterator). An iterator is sort of like a smart object that has a reference to the iterable and a "current position" within it, although under the covers they're rarely implemented that way. Calling next on an iterator effectively returns the value at the current position and advances the iterator to the next one (or the equivalent for however the iterator is actually implemented).

** What actually happens is, in effect, a try:/except StopIteration:, because calling next on an iterator that's done raises StopIteration. Of course it's implemented in C (or Java or .NET or RPython, for other Python implementations), and the C actually uses some special magic code for looping over an iterator that makes it a little faster, but almost nobody ever needs to think about that part.

于 2013-08-27T18:03:23.313 回答