我们知道以下代码只是逐行加载数据,而不是将它们全部加载到内存中。即线读数将以某种方式标记为OS的“可删除”
def fileGen( file ):
for line in file:
yield line
with open("somefile") as file:
for line in fileGen( file ):
print line
但是,如果我们将fileGen的定义修改为以下,我们可以验证这是否仍然是真的吗?
def fileGen( file ):
for line in csv.Reader( file ):
yield line
我们怎么知道csv.Reader是否会缓存它加载的数据?谢谢
的问候, 约翰
答案 0 :(得分:4)
找出csv.reader
正在做的最可靠的方法是阅读源代码。见_csv.c
,第773行。您将看到reader对象具有指向底层迭代器(通常是文件迭代器)的指针,并且每次需要另一行时它都会调用PyIter_Next
。因此它不会提前读取或以其他方式缓存它加载的数据。
找出csv.reader
正在做什么的另一种方法是创建一个可以在查询时报告的模拟文件对象。例如:
class MockFile:
def __init__(self): self.line = 0
def __iter__(self): return self
def next(self):
self.line += 1
print "MockFile line", self.line
return "line,{0}".format(self.line)
>>> r = csv.reader(MockFile())
>>> next(r)
MockFile line 1
['line', '1']
>>> next(r)
MockFile line 2
['line', '2']
这证实了我们从阅读csv
源代码中学到的东西:它只在调用自己的next
方法时才从底层迭代器请求下一行。
John明确表示(见评论)他关注的是csv.reader
是否使行保持活跃状态,以防止Python的内存管理器收集它们。
同样,您可以阅读代码(最可靠)或尝试实验。如果你看一下Reader_iternext
中_csv.c
的实现,你会看到lineobj
是底层迭代器返回的对象的名称,并且调用{{1}通过代码的每条路径。因此Py_DECREF(lineobj)
不会使csv.reader
保持活跃状态。
这是一个确认这一点的实验。
lineobj
所以你可以看到class FinalizableString(string):
"""A string that reports its deletion."""
def __init__(self, s): self.s = s
def __str__(self): return self.s
def __del__(self): print "*** Deleting", self.s
class MockFile:
def __init__(self): self.line = 0
def __iter__(self): return self
def next(self):
self.line += 1
return FinalizableString("line,{0}".format(self.line))
>>> r = csv.reader(MockFile())
>>> next(r)
*** Deleting line,1
['line', '1']
>>> next(r)
*** Deleting line,2
['line', '2']
没有挂在它从迭代器获取的对象上,如果没有其它东西让它们保持活着,那么它们会及时被垃圾收集。
我有一种感觉,就是你没有告诉我们这个问题。你能解释为什么你担心这个吗?