使用PyYAML
作为CLoader
解析器的YAML
,我试图加载YAML
文件,进行解析,然后将其写入单独的文件。 / p>
出于测试目的,我使用的是一个很大的YAML
文件,大于1GB
。
我正试图在命令行中显示一个进度条,以显示我的Python脚本正在运行并估计需要多长时间。
这是我当前的代码:
import yaml
import argparse
from tqdm import tqdm
from yaml import CLoader as Loader
def main():
parser = argparse.ArgumentParser(description='Takes in YAML files and uploads straight to Neo4J database')
parser.add_argument('-f', '--files', nargs='+', metavar='', required=True,
help='<Required> One or more YAML files to upload')
args = parser.parse_args()
for file_name in args.files:
with open(file_name, 'r') as stream:
print("Reading input file...")
with open('test2.txt', 'w') as wf:
print("Writing to output file...")
try:
for data in tqdm(yaml.load(stream, Loader=Loader)):
wf.write(data.get('primaryName') + '\n')
wf.write('++++++++++\n')
except yaml.YAMLError as exc:
print(exc)
if __name__ == "__main__":
main()
现在发生的是,有一个tqdm
进度条显示在数据写入循环中,而不是yaml.load()
进程,这是花费最多的时间。
也就是说,很长一段时间,YAML
文件完全加载之前,没有进度条显示。
我希望找到一种解决方案,以便能够将进度条包装在我无法访问的功能(在这种情况下为yaml.load()
)周围。
我做错什么了吗?任何建议都将是很棒并且值得赞赏的。
答案 0 :(得分:0)
否,无法将进度条包裹在您无法访问的代码周围。
此外,当您遍历可迭代对象(不在这里)时,只能使用iterable-based接口连接tqdm。因此,您必须使用基于update
的界面:
with tqdm(total=100) as pbar:
for i in range(10):
pbar.update(10)
问题是,如何让PyYAML调用该pbar.update
?
理想情况下,您想找到一个挂接加载过程的位置,您可以在其中调用<{1>}。如果这不可能,则您将不得不做一些丑陋的事情(例如fork pbar.update
并添加到其API,或者在运行时通过monkeypatching进行相同的操作),或切换到其他库。但这应该是可能的。
最明显的选择是创建自己的PyYAML
子类。 PyYAML的文档解释了此类的API,因此您可以覆盖那里的任何方法以发出一些进展,然后PyYAML.Loader
到基类。
但是不幸的是,他们都没有那么有前途。当然,您可以为每个令牌,每个事件或每个节点调用一次,但是在不知道有多少令牌,事件或节点的情况下,这无法让您知道到文件的距离。如果您想要一个不确定的进度微调器,那很好,但是如果您可以获取实际进度,并估计需要走多长时间,依此类推,那总会更好。
可以可以做的一件事是在其super
上进行Loader
子类调用tell
,以计算出到目前为止已读取了多少字节。
我在这台计算机上没有PyYAML,而且文档非常混乱,因此您可能需要进行一些实验,但这应该是这样的:
stream
但是我不知道如何获取PyYAML来将回调传递到class ProgressLoader(yaml.CLoader):
def __init__(self, stream, callback):
super().__init__(stream)
# __ because who knows what names the base class is using?
self.__stream = stream
self.__pos = 0
self.__callback = callback
def get_token(self):
result = super().get_token()
pos = self.__stream.tell()
self.__callback(pos - self.__pos)
self.__pos = pos
return result
构造函数中,因此您必须执行以下操作:
ProgressLoader
但是无论如何,只要我们转到文件,不搞乱记录在案的加载器类型,而只写一个文件包装器,可能会更容易。
The docs for file objects相当密集,但是至少它们很清楚-实际工作非常简单:
with open(file_name, 'r') as stream:
size = os.stat(stream.fileno()).st_size
with tqdm(total=size) as progress:
factory = lambda stream: ProgressLoader(stream, progress.update)
data = yaml.load(stream, Loader=factory)
现在:
class ProgressFileWrapper(io.TextIOBase):
def __init__(self, file, callback):
self.file = file
self.callback = callback
def read(self, size=-1):
buf = self.file.read(size)
if buf:
self.callback(len(buf))
return buf
def readline(self, size=-1):
buf = self.file.readline(size)
if buf:
self.callback(len(buf))
return buf
这当然不是完美的。我们在这里假设所有工作都是从磁盘读取文件,而不是对其进行解析。可能足够接近真实,我们才能摆脱它,但是如果不是,您将拥有其中一个进度条,可以将其压缩到几乎100%,然后长时间呆在那里。 1
1。这不仅令人讨厌,而且与Windows和其他Microsoft产品紧密相关,以至于他们可能起诉您窃取其外观和感觉。 :)