python linecache读取过程

最近使用Python编写日志处理脚本时,对Python的几种读取文件的方式进行了实验。其中,linecache的行为引起了我的注意。
Python按行读取文件的经典方式有以下几种:

with open('blabla.log', 'r') as f:
    for line in f.readlines():
        ## do something
with open('blabla.log', 'r') as f:
    for line in f:
      ## do something
with open('blabla.log', 'r') as f:
    while 1:
        line = f.readline()
        if not line:
          break
        ## do something

以上几种方式都不支持对于文件按行随机访问。在这样的背景下,能够支持访直接访问某一行内容的linecache模块是一种很好的补充。
我们可以使用linecache模块的getline方法访问某一具体行的内容,官方文档中给出了如下用法:

>>> import linecache
>>> linecache.getline('/etc/passwd', 4)

在使用过程中我注意到,基于linecache的getline方法的日志分析会在跑满CPU资源之前首先占用大量内存空间,也就是在CPU使用率仍然很低的情况下,内存空间就会被迅速地消耗。
这一现象引起了我的兴趣。我猜测linecache在随机读取文件时,是首先依序将文件读入内存,之后寻找所要定位的行是否在内存当中。若不在,则进行相应的替换行为,直至寻找到所对应的行,再将其返回。
对linecache代码的阅读证实了这一想法。
在linecache.py中,我们可以看到getline的定义为:

def getline(filename, lineno, module_globals=None):
    lines = getlines(filename, module_globals)
    if 1 <= lineno <= len(lines):
        return lines[lineno-1]
    else:
        return ''

不难看出,getline方法通过getlines得到了文件行的List,以此来实现对于文件行的随机读取。继续查看getlines的定义。

def getlines(filename, module_globals=None):
    """Get the lines for a file from the cache.
    Update the cache if it doesn't contain an entry for this file already."""

    if filename in cache:
        return cache[filename][2]
    else:
        return updatecache(filename, module_globals)

由此可见,getlines方法会首先确认文件是否在缓存当中,如果在则返回该文件的行的List,否则执行updatecache方法,对缓存内容进行更新。因此,在程序启动阶段,linecache不得不首先占用内存对文件进行缓存,才能进行后续的读取操作。
而在updatecache方法中,我们可以看到一个有趣的事实是:

def updatecache(filename, module_globals=None):
    """Update a cache entry and return its list of lines.
    If something's wrong, print a message, discard the cache entry,
    and return an empty list."""

    ## ... 省略...

    try:
        fp = open(fullname, 'rU')
        lines = fp.readlines()
        fp.close()
    except IOError, msg:
##      print '*** Cannot open', fullname, ':', msg
        return []
    if lines and not lines[-1].endswith('\n'):
        lines[-1] += '\n'
    size, mtime = stat.st_size, stat.st_mtime
    cache[filename] = size, mtime, lines, fullname
    return lines

也就是说,linecache依然借助了文件对象的readlines方法。这也给了我们一个提示,当文件很大不适用readlines方法直接获取行的List进行读取解析时,linecache似乎也并不会成为一个很好的选择。

posted on 2017-04-17 22:08  WenNn  阅读(8539)  评论(2编辑  收藏  举报

导航