如何组织我的pyparsing函数?
我正在用Python和pyparsing来解析一个文件(这个文件是PSAT在Matlab中的报告文件,但这不是重点)。这是我目前的代码。我觉得它有点乱,想请教一下怎么改进。特别是,我应该怎么组织我的语法定义,用pyparsing来写?
我是不是应该把所有的语法定义放在一个函数里?如果这样的话,这个函数会变得非常庞大。如果不这样做,那我该怎么拆分呢?目前我是按照文件的不同部分来分的。是不是值得为了只调用一次的功能而写很多函数呢?这两种方式我都觉得不太对劲。
我是不是应该把所有的输入和输出代码放在一个单独的文件里,而不是和其他类的函数放在一起?这样可以让这个类的目的更加清晰。
我也想知道有没有更简单的方法来解析文件,做一些基本检查,然后把数据存储在一个类里。我发现我花了很多时间在这上面。
(如果大家同意,我也会接受“这样就够了”或者“用X代替pyparsing”的回答)
1 个回答
我觉得你可以选择用一个大方法来创建解析器,或者像现在这样分步骤进行,都是可以的。
我注意到你定义了一些很有用的辅助工具,比如 slit(我猜是“抑制字面量”),stringtolits 和 decimaltable。这些看起来不错。
我喜欢你使用结果名称,这样能大大增强你解析后代码的健壮性。我建议你使用在 pyparsing 1.4.7 中新增的快捷方式,这样你可以把
busname.setResultsName("bus1")
替换成
busname("bus1")
这样可以让你的代码看起来更简洁。
我建议你回头看看你的解析动作,看看哪里用数字索引来访问单个标记,然后改成使用结果名称。比如这里有一个例子,GetStats 返回 (ngroup + sgroup).setParseAction(self.process_stats)
。process_stats 中有类似这样的引用:
self.num_load = tokens[0]["loads"]
self.num_generator = tokens[0]["generators"]
self.num_transformer = tokens[0]["transformers"]
self.num_line = tokens[0]["lines"]
self.num_bus = tokens[0]["buses"]
self.power_rate = tokens[1]["rate"]
我喜欢你把值和统计数据进行了分组,但可以给它们起个名字,比如“network”和“soln”。这样你可以把这个解析动作的代码写成(我也把它转换成了我觉得更易读的对象属性表示法,而不是字典元素表示法):
self.num_load = tokens.network.loads
self.num_generator = tokens.network.generators
self.num_transformer = tokens.network.transformers
self.num_line = tokens.network.lines
self.num_bus = tokens.network.buses
self.power_rate = tokens.soln.rate
还有一个风格问题:为什么有时候你使用显式的 And 构造函数,而不是用 '+' 操作符呢?
busdef = And([busname.setResultsName("bus1"),
busname.setResultsName("bus2"),
integer.setResultsName("linenum"),
decimaltable("pf qf pl ql".split())])
这同样可以写成:
busdef = (busname("bus1") + busname("bus2") +
integer("linenum") +
decimaltable("pf qf pl ql".split()))
总体来说,我觉得这个文件的复杂度大致是这样的。我有一个类似的格式(可惜是专有的,不能分享),我也是像你这样分块构建代码,但用一个大方法,像这样:
def parser():
header = Group(...)
inputsummary = Group(...)
jobstats = Group(...)
measurements = Group(...)
return header("hdr") + inputsummary("inputs") + jobstats("stats") + measurements("meas")
在像这样的一个大型解析器中,Group 构造特别有帮助,可以为解析数据的每个部分的结果名称建立一种命名空间。