使用genfromtxt:如何禁用缓存

5
我已确认genfromtxt函数(以及由此派生的函数)会在本地缓存远程文件,并在后续调用中使用本地副本而不检查其是否发生更改。
通过查看源文件npyio.py,似乎是因为处理请求的DataSource对象在创建时没有传递相关参数导致的。 当然,修改库源代码以禁用缓存很容易,但这意味着我必须在每次升级后重复此操作。
除了每次删除缓存目录之外,还有其他解决方案吗?

要禁用缓存,必须修改链接的npyio.py中第1360行的内容为: fhd = iter(np.lib._datasource.open(fname, 'rbU',None)) - NameOfTheRose
3
你自己打开文件,并将该对象传递给 genfromtxt,这样做有帮助吗? - hpaulj
是的,它似乎可以工作,并且与下面的建议结合起来可能回答了问题。成本在于原始函数的某些功能被复制,必须研究和理解_genfromtxt_源代码。 - NameOfTheRose
2个回答

3
我认为这个问题实际上由两部分组成:
  1. 如果库的功能与所需行为不完全匹配,该怎么办?

  2. 特别是如何处理genfromtxt的缓存行为?

关于第一点,包装(可能带有注入)比修补库更具弹性(除非在库的repo中进行了修补)。

因此,可以像这样包装genfromtxt

def patched_gen_from_text(*args, **kwargs):
    # Do something regarding caching
    return numpy.genfromtxt(*args, **kwargs)

你甚至可以将其作为numpy.genfromtext注入,而无需修改源代码(虽然我不建议这样做):
import numpy 

numpy.genfromtxt = patched_gen_from_text

关于第二点,这取决于您对远程文件系统的访问权限(例如,您是否可以在那里运行进程?是否可以挂载它?)以及所需速度和确定性之间的权衡。

例如,在一个极端情况下,您的修补版本可以无条件地删除本地文件(确切但慢)。或者,您可以请求远程文件的更新时间和长度,并查看它们与本地文件的对应关系。在另一个极端情况下,您可能可以在另一台计算机上通过RPC运行md5检查。

您可能想要查看filecmp以获取不同的比较选项,以及一些情况下可能的实际构建块。


澄清了我想要表达的意思。 - Ami Tavory
谢谢,我很感激第一部分的见解。关于第二部分,我可能需要在从genfromtxt返回时实现对临时文件的无条件删除,并按照您的建议进行包装。 - NameOfTheRose

1

研究库源码后,我意识到可以通过修改numpy的datasource模块中名为open的小助手函数的默认值来实现所需的行为。如上所述,这可以在不修改库源码的情况下实现。以下是我编写的代码:

import numpy
from numpy.lib._datasource import DataSource
#def open(path, mode='r', destpath=os.curdir):
def openm(path, mode='r', destpath=None):
  ds = DataSource(destpath)
  return ds.open(path, mode)
numpy.lib._datasource.open=openm

在调用genfromtxt或其派生函数之前,必须包含它。

但是我的研究进一步揭示了这些函数非常缓慢,在Windows中禁用缓存会产生警告 - 这不是由于上面的重新定义,而似乎与mktemp函数在Windows中的实现方式有关。此外,缓存文件和相关的临时目录没有被删除。

这似乎与处理请求的数据源在文件读取和关闭之前就超出了范围(因此被删除)有关。当数据源的析构函数执行时,它尝试删除数据源的目录,由于文件句柄仍然打开,所以出现警告。我考虑将数据源实例化移动到genfromtxt函数中。 我确认将数据源实例化移动到genfromtxt函数中在Windows和Linux下都可以正常工作,因此我认为这是正确的解决方案。

我得出结论,最干净的方法是修改numpy库并将缓存参数添加到genfromtxt

以下是对npyio.py进行的必要修改,只需要修改几行即可。 genfromtxt函数原型更改为

def genfromtxt(fname, dtype=float, comments='#', delimiter=None,
           skiprows=0, skip_header=0, skip_footer=0, converters=None,
           missing='', missing_values=None, filling_values=None,
           usecols=None, names=None,
           excludelist=None, deletechars=None, replace_space='_',
           autostrip=False, case_sensitive=True, defaultfmt="f%i",
           unpack=None, usemask=False, loose=True, invalid_raise=True):

添加了一个新的缓存参数

def genfromtxt(fname, dtype=float, comments='#', delimiter=None,
           skiprows=0, skip_header=0, skip_footer=0, converters=None,
           missing='', missing_values=None, filling_values=None,
           usecols=None, names=None,
           excludelist=None, deletechars=None, replace_space='_',
           autostrip=False, case_sensitive=True, defaultfmt="f%i",
           unpack=None, usemask=False, loose=True, invalid_raise=True,cache=True):

并且打开文件/URL的代码行会改变

if isinstance(fname, basestring):
    fhd = iter(np.lib._datasource.open(fname, 'rbU')
    own_fhd = True

if isinstance(fname, basestring):
    ds=DataSource('.' if cache==True else None)
    fhd = iter(ds.open(fname, 'rbU'if cache==True else 'rbUD'))
    own_fhd = True

“D”是Windows特有的标志,用于打开(在Linux中无害),标记文件为临时文件,在文件句柄关闭时自动删除。
包装函数版本
def mgenfromtxt(fname,cache=True,**karg):
    if cache==False and isinstance(fname, basestring) and numpy.DataSource()._isurl(fname):
       ds=numpy.DataSource(None)
       fhd = iter(ds.open(fname,'rbUD'))
       l1=numpy.genfromtxt(fhd,**karg)
       fhd.close()
       del ds
       return l1
    else:
       return(numpy.genfromtxt(fname,**karg))  

我想感谢回答我的问题Python文件打开函数模式并澄清模式字符“D”的人们。 - NameOfTheRose
如果不想修改库,可以在调用函数中实例化数据源并打开URL,然后将文件句柄传递给@hpaulj建议的genfromtext。 - NameOfTheRose
一个人也可以像@ami-tavory在他的回答中建议的那样,使用包装函数(可能带有注入)来实现这个功能。我感谢他们俩。 - NameOfTheRose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接