Python 中的动态语义错误

问题描述 投票:0回答:2

我在面试时遇到了这个问题。这个问题看起来很有趣。所以,我把它发布在这里。

考虑会产生语义错误的运算,例如除以零。默认情况下,python 编译器会给出“无效操作”之类的输出。我们可以控制Python编译器给出的输出,比如打印一些其他错误消息,跳过除零操作,并继续执行其余指令吗?
另外,我如何评估运行时语义检查的成本? 这里有很多Python专家。我希望有人能对此有所启发。

python semantics
2个回答
6
投票

我们可以控制Python编译器给出的输出,比如打印一些其他错误消息,跳过除零操作,并继续执行其余指令吗?

不,你不能。您可以使用

try...except
块手动包装每个危险命令,但我假设您正在谈论自动恢复到 try...except 块中的特定行,甚至完全自动恢复。

当错误消失以致调用

sys.excepthook

时,或者任何外部作用域(如果您尽早捕获它),内部作用域都消失了。您可以在 CPython 中使用

sys.settrace
 更改行号,尽管这
只是一个实现细节
,但由于外部作用域已经消失,因此没有可靠的恢复机制。 如果您尝试使用幽默的

goto

愚人节模块(使用我刚才描述的方法)来跳转块

甚至在文件中
from goto import goto, label try: 1 / 0 label .foo print("recovered") except: goto .foo

您收到错误:

Traceback (most recent call last): File "rcv.py", line 9, in <module> goto .foo File "rcv.py", line 9, in <module> goto .foo File "/home/joshua/src/goto-1.0/goto.py", line 272, in _trace frame.f_lineno = targetLine ValueError: can't jump into the middle of a block

所以我很确定这是不可能的。


另外,我如何评估运行时语义检查的成本?

我不知道那是什么,但您可能正在寻找

line_profiler

:
import random from line_profiler import LineProfiler profiler = LineProfiler() def profile(function): profiler.add_function(function) return function @profile def foo(a, b, c): if not isinstance(a, int): raise TypeError("Is this what you mean by a 'run-time semantic check'?") d = b * c d /= a return d**a profiler.enable() for _ in range(10000): try: foo(random.choice([2, 4, 2, 5, 2, 3, "dsd"]), 4, 2) except TypeError: pass profiler.print_stats()

输出:

Timer unit: 1e-06 s File: rcv.py Function: foo at line 11 Total time: 0.095197 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 11 @profile 12 def foo(a, b, c): 13 10000 29767 3.0 31.3 if not isinstance(a, int): 14 1361 4891 3.6 5.1 raise TypeError("Is this what you mean by a 'run-time semantic check'?") 15 16 8639 20192 2.3 21.2 d = b * c 17 8639 20351 2.4 21.4 d /= a 18 19 8639 19996 2.3 21.0 return d**a

因此,在这种情况下,“运行时语义检查”将占用运行时间的 36.4% 
foo


如果您想手动对大于您使用
timeit

的特定块进行计时,但小于您想要的探查器所需的时间,我建议不要使用两个

time.time()
调用(这是一种非常不准确的方法)
Steven D'Aprano 的秒表上下文管理器


1
投票
f(a,b)

:


def f(a: int, b: int): """ @param a: @param b: """ try: c = a / b print(c) except ZeroDivisionError: print("You idiot, you can't do that ! :P") if __name__ == '__main__': f(1, 0)


>>> from cheese import f >>> f(0, 0) You idiot, you can't do that ! :P >>> f(0, 1) 0.0 >>> f(1, 0) You idiot, you can't do that ! :P >>> f(1, 1) 1.0
这是一个如何通过使用 
ZeroDivisionError

制作异常情况来捕获零除法的示例。


我不会讨论任何用于制作记录器的特定工具,但您确实可以理解与这种检查相关的成本。您可以在函数的开头放置

start = time.time()

,在函数末尾放置

end = time.time()
。如果取差值,您将得到以秒为单位的执行时间。

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.