【问题标题】:Don't understand Python's csv.reader object不懂 Python 的 csv.reader 对象
【发布时间】:2014-12-03 06:08:04
【问题描述】:

我在 python 的内置 csv 模块中遇到了一种我以前从未注意到的行为。通常,当我在 csv 中阅读时,它几乎一字不差地遵循doc's,使用“with”打开文件,然后使用“for”循环遍历阅读器对象。但是,我最近尝试连续两次遍历 csv.reader 对象,结果发现第二个“for”循环什么也没做。

import csv

with open('smallfriends.csv','rU') as csvfile:
readit = csv.reader(csvfile,delimiter=',')

for line in readit:
    print line

for line in readit:
    print 'foo'

控制台输出:

Austins-iMac:Desktop austin$ python -i amy.py 
['Amy', 'James', 'Nathan', 'Sara', 'Kayley', 'Alexis']
['James', 'Nathan', 'Tristan', 'Miles', 'Amy', 'Dave']
['Nathan', 'Amy', 'James', 'Tristan', 'Will', 'Zoey']
['Kayley', 'Amy', 'Alexis', 'Mikey', 'Sara', 'Baxter']
>>>
>>> readit
<_csv.reader object at 0x1023fa3d0>
>>> 

所以第二个'for'循环基本上什么都不做。我的一个想法是 csv.reader 对象在被读取一次后从内存中释放。但情况并非如此,因为它仍然保留它的内存地址。我发现了一个提到类似问题的post。他们给出的原因是,一旦读取了对象,指针就会停留在内存地址的末尾,准备将数据写入对象。这个对吗?有人可以更详细地了解这里发生了什么吗?有没有办法将指针推回到内存地址的开头重新读取它?我知道这样做是不好的编码实践,但我主要只是好奇,想了解更多关于 Python 底层发生的事情。

谢谢!

【问题讨论】:

  • 一旦你在第一个循环中使用了迭代器readit,它基本上是空的。
  • 那么可以认为是“只读一次”吗?
  • 是的,阅读器对象类似于(如果不是)生成器对象,根据请求从文件中提取和解析行(通过next())。一旦使用它(运行整个文件),如果您想再次处理它,您需要从头重新启动文件,或者将所有数据读入内存。
  • 我在问题中包含的帖子提到了有关 reset() 对象的一些内容。你将如何使用它?
  • 你需要考虑你想要做什么。通常,最好一次性完成任何必要的处理。如果您要足够频繁地重复使用数据,请继续将其读入内存(例如作为列表)。

标签: python pointers object csv memory


【解决方案1】:

我将尝试回答您关于读者在做什么以及为什么reset()seek(0) 可能会有所帮助的其他问题。在最基本的形式中,csv 阅读器可能看起来像这样:

def csv_reader(it):
    for line in it:
        yield line.strip().split(',')

也就是说,它接受任何生成字符串的迭代器并为您提供一个生成器。它所做的只是从您的迭代器中获取一个项目,处理它并返回该项目。当it 被消耗时,csv_reader 将退出。读者不知道迭代器是从哪里来的,也不知道如何正确地制作一个新的,所以它甚至不会尝试重置自己。这是留给程序员的。

我们可以在读者不知道的情况下修改迭代器,或者只是创建一个新的阅读器。这里有一些例子来证明我的观点。

data = open('data.csv', 'r')
reader = csv.reader(data)

print(next(reader))               # Parse the first line
[next(data) for _ in range(5)]    # Skip the next 5 lines on the underlying iterator
print(next(reader))               # This will be the 7'th line in data
print(reader.line_num)            # reader thinks this is the 2nd line
data.seek(0)                      # Go back to the beginning of the file
print(next(reader))               # gives first line again

data = ['1,2,3', '4,5,6', '7,8,9']
reader = csv.reader(data)         # works fine on lists of strings too
print(next(reader))               # ['1', '2', '3']

一般来说,如果您需要第二遍,最好关闭/重新打开文件并使用新的 csv 阅读器。它干净,并确保良好的簿记。

【讨论】:

    【解决方案2】:

    对 csvreader 的迭代只是简单地对底层文件对象中的行进行迭代。 在每次迭代中,阅读器从文件中获取下一行,转换并返回它。

    因此遍历 csvreader 遵循与 iterating over files 相同的约定。 也就是说,一旦文件到达结尾,您必须在第二次迭代之前寻找开始。

    以下应该可以,虽然我还没有测试过:

    import csv
    
    with open('smallfriends.csv','rU') as csvfile:
        readit = csv.reader(csvfile,delimiter=',')
    
        for line in readit:
            print line
    
        # go back to the start of the file
        csvfile.seek(0)
    
        for line in readit:
            print 'foo
    

    【讨论】:

      【解决方案3】:

      如果不是太多数据,可以随时读入列表:

      import csv
      
      with open('smallfriends.csv','rU') as csvfile:
          readit = csv.reader(csvfile,delimiter=',')
          csvdata = list(readit)
      
          for line in csvdata :
              print line
      
          for line in csvdata :
              print 'foo'
      

      【讨论】:

      • 是的,这始终是一个选项,但我更感兴趣的是在较低级别发生的事情以及为什么无法第二次迭代对象。
      猜你喜欢
      • 2021-04-09
      • 1970-01-01
      • 2016-11-18
      • 2018-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-19
      • 2021-08-18
      相关资源
      最近更新 更多