IndexError:列表索引超出范围,但列表长度OK

时间:2016-12-08 04:17:57

标签: python-3.x list-comprehension

编程新手,希望更深入地了解发生的事情。

目标:打开文件并打印前10行。 (类似于头部命令)

代码:

with open('file') as f: 
      for i in range(0,10): 
          print([line.strip('\n') for line in f][i])

结果:打印第一行罚款,然后返回超出范围错误

文件:是一个简单的文本文件,包含20行,每行不超过50个字符

仅供参考 - 删除范围行并打印类型(列表)和长度(20)。打印的特定索引没有问题(除非连续> 1)

能够使用不同的代码获得所需的结果,但尝试使用/ as

进行改进

1 个答案:

答案 0 :(得分:4)

您实际上可以迭代文件。你应该在这做什么。

with open('file') as f:
    for i, line in enumerate(file, start=1):
        # Get out of the loop if we hit 10 lines
        if i >= 10:
            break
        # Line already has a '\n' at the end
        print(line, end='')

您的代码失败的原因是您的列表理解:

[line.strip('\n') for line in f]

第一次通过循环消耗文件中所有行。现在你的文件没有更多的行了,所以下次通过它会创建一个文件中所有行的列表,并尝试获取[1] st元素。但这不存在,因为文件末尾没有行。

如果你想保持你的代码主要是你可以做的

lines = [line.rstrip('\n') for line in f]
for i in range(10):
    print(lines[i])

但这也很愚蠢,因为你可以做到

lines = f.readlines()

但如果你只想要达到第10行,傻了,因为你可以这样做:

with open('file') as f:
    print('\n'.join(f.readlines()[:10]))

进一步解释:

您可以修复代码的最短和最差方式是添加一行代码:

with open('file') as f: 
      for i in range(0,10):
          f.seek(0)  # Add this line
          print([line.strip('\n') for line in f][i])

现在您的代码可以运行 - 但这是一种让您的代码正常工作的可怕的方式。 原因你的代码首先没有按照你期望的方式工作,那就是文件是可消耗的迭代器。这意味着,当你从他们那里读取时,你最终会用完所有东西来阅读。这是一个简单的例子:

import io

file = io.StringIO('''
This is is a file
It has some lines
okay, only three.
'''.strip())

for line in file:
    print(file.tell(), repr(line))

此输出

18 'This is is a file\n'
36 'It has some lines\n'
53 'okay, only three.'

现在,如果您尝试从文件中读取:

print(file.read())

你会发现它没有输出任何东西。那是因为你“消耗”了这个文件。我的意思是它显然仍然在磁盘上,但是迭代器已经到了文件的末尾。但如图所示,您可以在文件中查找。

print(file.tell())
file.seek(0)
print(file.tell())
print(file.read())

您将看到整个文件已打印出来。但那些其他职位呢?

file.seek(36)
print(file.read())  # => okay, only three.

作为旁注,您还可以指定阅读量:

file.seek(36)
print(file.read(4))  # => okay
print(file.tell())  # => 40

因此,当我们从文件中读取或迭代它时,我们使用迭代器并到达文件的末尾。让我们开始使用您的新工具,然后回到原始代码并探索正在发生的事情。

with open('file') as f:
    print(f.tell())
    lines = [line.rstrip('\n') for line in f]
    print(f.tell())
    print(len([line for line in f]))
    print(lines)

您会看到您在文件中的其他位置。第二个列表理解产生一个空列表。那是因为当评估列表理解时,它会立即执行。所以当你这样做时:

for i in range(10):
    print([line.strip('\n') for line in f][i])

你第一次做什么,i = 0然后列表理解读到文件的末尾。现在它需要列表的[0]元素或文件中的第一行。但是你的文件迭代器位于文件的末尾。

现在我们回到列表的开头i = 1。现在我们迭代到文件的末尾,但我们已经在最后,所以没有要读取的行,我们有一个空列表[],我们试图获取[0]的元素。但那里什么都没有。所以我们得到IndexError

列表推导可以有用,但是当你开始时,编写for循环通常会更容易,然后将其转换为列表理解。所以你可能会这样写:

with open('file') as f:
    for i, line in enumerate(file, start=10):
        if i < 10:
            print(line.rstrip())

现在,我们不应该在列表理解中打印,所以我们将收集所有内容。我们从我们想要的东西开始:

[line.rstrip()

现在添加位:

[line.rstrip() for i, line in enumerate(f)

最后添加过滤器和我们的右括号:

[line.rstrip() for i, line in enumerate(f) if i < 10]

有关列表推导的更多信息,这是一个很棒的资源:http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/