我创建的解析器从文件中读取记录的国际象棋游戏。 API的使用方式如下:
import chess.pgn
pgn_file = open("games.pgn")
first_game = chess.pgn.read_game(pgn_file)
second_game = chess.pgn.read_game(pgn_file)
# ...
有时会遇到非法移动(或其他问题)。什么是好的Pythonic方式来处理它们?
遇到错误时立即引发异常。但是,这会使每个问题都致命,因为执行会停止。通常,仍然有用的数据已被解析并可以返回。此外,您不能简单地继续解析下一个数据集,因为我们仍然处于一些半读数据的中间。
在游戏结束时累积异常并提升它们。这使得错误再次致命,但至少你可以抓住它并继续解析下一场比赛。
引入一个像这样的可选参数:
game = chess.pgn.read_game(pgn_file, parser_info)
if parser_info.error:
# This appears to be quite verbose.
# Now you can at least make the best of the sucessfully parsed parts.
# ...
这些或其他方法是否在野外使用?
答案 0 :(得分:9)
最Pythonic方式是logging模块。在评论中已经提到过,但遗憾的是没有足够强调这一点。 warnings优先考虑的原因有很多:
日志记录模块的基本用例如下:
import logging
logger = logging.getLogger(__name__) # module-level logger
# (tons of code)
logger.warning('illegal move: %s in file %s', move, file_name)
# (more tons of code)
这将打印如下消息:
WARNING:chess_parser:illegal move: a2-b7 in file parties.pgn
(假设您的模块名为chess_parser.py)
最重要的是您不需要在解析器模块中执行任何其他操作。您声明您正在使用日志系统,您正在使用具有特定名称的记录器(与此示例中的解析器模块名称相同),并且您正在向其发送警告级别消息。您的模块不必知道如何处理,格式化这些消息并将其报告给用户。或者如果他们被报道的话。例如,您可以配置日志记录模块(通常在程序的最开头)使用不同的格式并将其转储到文件中:
logging.basicConfig(filename = 'parser.log', format = '%(name)s [%(levelname)s] %(message)s')
突然,在没有对模块代码进行任何更改的情况下,您的警告消息将保存到具有不同格式的文件中,而不是打印到屏幕上:
chess_parser [WARNING] illegal move: a2-b7 in file parties.pgn
如果您愿意,也可以禁止警告:
logging.basicConfig(level = logging.ERROR)
您的模块警告将被完全忽略,而模块中的任何ERROR或更高级别的消息仍将被处理。
答案 1 :(得分:6)
实际上,那些是致命错误 - 至少,只要能够重现正确的游戏;另一方面,也许玩家确实做了非法移动,当时没有人注意到(这会使其成为警告,而不是致命的错误)。
鉴于可能存在致命错误(文件已损坏)和警告(非法移动,但随后的移动显示与此移动的一致性(换句话说,用户错误,当时没有人抓住它))我建议第一个和第二个选项的组合:
如果您没有遇到致命错误,那么您可以在最后返回游戏,加上任何警告/非致命错误:
return game, warnings, errors
但是,如果你遇到致命错误怎么办?
没问题:创建一个自定义异常,您可以将游戏的可用部分和任何其他警告/非致命错误附加到:
raise ParsingError(
'error explanation here',
game=game,
warnings=warnings,
errors=errors,
)
然后当你发现错误时,你可以访问游戏的可恢复部分,以及警告和错误。
自定义错误可能是:
class ParsingError(Exception):
def __init__(self, msg, game, warnings, errors):
super().__init__(msg)
self.game = game
self.warnings = warnings
self.errors = errors
并在使用中:
try:
first_game, warnings, errors = chess.pgn.read_game(pgn_file)
except chess.pgn.ParsingError as err:
first_game = err.game
warnings = err.warnings
errors = err.errors
# whatever else you want to do to handle the exception
这类似于subprocess
模块处理错误的方式。
为了能够在游戏致命错误后检索和解析后续游戏,我建议您更改API:
这样一来,如果你有一个五个游戏的文件,两个游戏就死了,你仍然可以尝试解析游戏3,4和5。
答案 2 :(得分:3)
我提供了赏金,因为我想知道这是否真的是最好的方法。但是,我也在编写解析器,所以我需要这个功能,这就是我提出的。
warnings
module正是您想要的。
一开始让我远离它的原因是文档中使用的每个示例警告都显示为these:
Traceback (most recent call last):
File "warnings_warn_raise.py", line 15, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
...这是不受欢迎的,因为我不希望它是UserWarning
,我想要自己的自定义警告名称。
以下是解决方案:
import warnings
class AmbiguousStatementWarning(Warning):
pass
def x():
warnings.warn("unable to parse statement syntax",
AmbiguousStatementWarning, stacklevel=3)
print("after warning")
def x_caller():
x()
x_caller()
给出:
$ python3 warntest.py
warntest.py:12: AmbiguousStatementWarning: unable to parse statement syntax
x_caller()
after warning
答案 3 :(得分:3)
我不确定解决方案是否是pythonic,但我经常稍作修改使用它:解析器在生成器中完成它的工作并产生结果和状态代码。接收代码决定失败项目的内容:
def process_items(items)
for item in items:
try:
#process item
yield processed_item, None
except StandardError, err:
yield None, (SOME_ERROR_CODE, str(err), item)
for processed, err in process_items(items):
if err:
# process and log err, collect failed items, etc.
continue
# further process processed
更通用的方法是练习使用设计模式。 Observer的简化版本(当您注册特定错误的回调时)或Visitor(访问者有处理特定错误的方法,请参阅SAX解析器以获取见解)的简化版本可能是一个清晰且易于理解的解决方案。
答案 4 :(得分:0)
没有库,很难干净地完成这项工作,但仍然有可能。
根据具体情况,有不同的处理方法。
方法1:
将while循环的所有内容放在以下内容中:
while 1:
try:
#codecodecode
except Exception as detail:
print detail
方法2:
与方法1相同,除了具有多个try / except东西,因此它不会跳过太多代码&amp;你知道错误的确切位置。
抱歉,匆忙,希望这有帮助!