繁体   English   中英

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

[英]why the list behaves differently in python and Java?

我正在学习脚本语言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]

从我看来,列表是否没有传递为对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循环内的每次迭代中,我都在增加列表大小,因此迭代应该永远正确吗?

python for循环评估表达式,该表达式产生仅循环一次的可迭代项。 你可以操纵lst在循环对象,而不会影响结果for是循环结束了。 这不同于Java for构造(这与Python for语句有很大不同,实际上是Foreach构造),后者为每次迭代评估3个关联表达式。

在第一个示例中,您创建了一个range()结果,并且一旦创建该结果,就不会为每次循环迭代更新。

在第二个示例中,您使用全长切片( lst[:] )创建了lst的副本,以使循环迭代。 不会为每个循环迭代重新创建副本。

但是,这里有一个警告。 for循环在要迭代的对象上调用iter() 对于列表对象,结果列表迭代器的确保留了对原始列表的引用以及迭代索引。 每当for循环使迭代器前进next()在其上调用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在循环中的扩展而不断产生下一个元素,如果我没有在循环中添加长度限制,那么它将永远持续下去。

range(len(lst))创建一次范围,然后对其进行迭代,而在java list.size()则在每次迭代时求值

>列表是否未传递为python中for循环的引用?

所有对象都通过在Python引用传递,并在Python 一切都是对象。 (不是Java基本值,但即使是普通整数和浮点值也是Python中的对象。)

>在for循环内的每次迭代中,我都会增加列表大小,因此迭代应该永远正确吗?

您正在增加l的大小,这是正确的,但是l [:]仅被评估一次,并产生l的浅表副本。 该副本不会在循环中更改,因此在循环内部更改为l不会更改循环变量将采用的值集。

在该循环中将l [:]更改为l,然后您会看到很多输出。

通过将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中,由range函数返回的list )。

但是即使不知道,您仍然可以看到len(lst)在开始时仅被评估一次,以创建迭代器,而Java list.size()等效项在每次循环中list.size()评估。


如果要使用Java样式的for循环,则必须显式编写while循环。


* iter在所有可迭代对象(列表,范围,甚至另一个迭代器)上创建一个迭代器。 迭代器有点像一个智能对象,其中引用了其中的可迭代对象和“当前位置”,尽管在幕后他们很少以这种方式实现。 在迭代器上调用next有效地返回当前位置的值,并将迭代器前进到下一个迭代器(或等效项,但实际上已实现该迭代器)。

**实际上发生的实际上是一个try: / except StopIteration: ,因为在完成的迭代器上调用next会引发StopIteration 当然,它是用C语言实现的(对于其他Python实现,则是Java或.NET或RPython),而C语言实际上使用了一些特殊的魔术代码来循环遍历迭代器,这使其速度更快一些,但几乎没有人需要考虑那部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM