Nik*_*las 9 python error-handling warnings exception-handling
我创建的解析器从文件中读取记录的国际象棋游戏.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)
# ...
Run Code Online (Sandbox Code Playgroud)
有时会遇到非法移动(或其他问题).什么是好的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.
# ...
Run Code Online (Sandbox Code Playgroud)这些或其他方法是否在野外使用?
Lav*_*Lav 10
最Pythonic方式是日志记录模块.在评论中已经提到过,但遗憾的是没有足够强调这一点.警告有多种原因:
日志记录模块的基本用例如下所示:
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)
Run Code Online (Sandbox Code Playgroud)
这将打印如下消息:
WARNING:chess_parser:illegal move: a2-b7 in file parties.pgn
Run Code Online (Sandbox Code Playgroud)
(假设您的模块名为chess_parser.py)
最重要的是,您不需要在解析器模块中执行任何其他操作.您声明您正在使用日志系统,您正在使用具有特定名称的记录器(与此示例中的解析器模块名称相同),并且您正在向其发送警告级别消息.您的模块不必知道如何处理,格式化这些消息并将其报告给用户.或者如果他们被报道的话.例如,您可以配置日志记录模块(通常在程序的最开头)使用不同的格式并将其转储到文件中:
logging.basicConfig(filename = 'parser.log', format = '%(name)s [%(levelname)s] %(message)s')
Run Code Online (Sandbox Code Playgroud)
突然,在没有对模块代码进行任何更改的情况下,您的警告消息将保存到具有不同格式的文件中,而不是打印到屏幕上:
chess_parser [WARNING] illegal move: a2-b7 in file parties.pgn
Run Code Online (Sandbox Code Playgroud)
或者,如果您愿意,可以禁止警告:
logging.basicConfig(level = logging.ERROR)
Run Code Online (Sandbox Code Playgroud)
并且您的模块警告将被完全忽略,而模块中的任何ERROR或更高级别的消息仍将被处理.
实际上,这些都是致命的错误 - 至少,只要能够重现正确的游戏; 另一方面,也许玩家确实做了非法移动,当时没有人注意到(这会使其成为警告,而不是致命的错误).
鉴于致命错误(文件已损坏)和警告(非法移动的可能性,但随后的移动显示与该移动的一致性(换句话说,用户错误,当时没有人抓住它))我建议组合第一个和第二个选项:
如果您没有遇到致命错误,那么您可以在最后返回游戏,加上任何警告/非致命错误:
return game, warnings, errors
Run Code Online (Sandbox Code Playgroud)
但是,如果你遇到了致命的错误怎么办?
没问题:创建一个自定义异常,您可以将游戏的可用部分和任何其他警告/非致命错误附加到:
raise ParsingError(
'error explanation here',
game=game,
warnings=warnings,
errors=errors,
)
Run Code Online (Sandbox Code Playgroud)
然后当你发现错误时,你可以访问游戏的可恢复部分,以及警告和错误.
自定义错误可能是:
class ParsingError(Exception):
def __init__(self, msg, game, warnings, errors):
super().__init__(msg)
self.game = game
self.warnings = warnings
self.errors = errors
Run Code Online (Sandbox Code Playgroud)
并在使用中:
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
Run Code Online (Sandbox Code Playgroud)
这类似于subprocess
模块处理错误的方式.
为了能够在游戏致命错误后检索和解析后续游戏,我建议您更改API:
这样,如果你有一个五个游戏的文件和两个游戏死亡,你仍然可以尝试解析游戏3,4和5.