1

我遇到了这个作为一个面试问题。这个问题似乎很有趣。所以,我把它贴在这里。

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

4

2 回答 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 的秒表上下文管理器.

于 2013-09-22T16:08:31.680 回答
1

我只想使用一个例外,这个例子使用的是 python 3。对于 Python 2,只需删除函数参数后的注释。所以你的函数签名看起来像这样 -> 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.

我不会介绍任何用于制作记录器的特定工具,但您确实可以了解与这种检查相关的成本。您可以将 astart = time.time()放在函数的开头和end = time.time()结尾。如果您取差价,您将获得以秒为单位的执行时间。

我希望这会有所帮助。

于 2013-09-22T16:10:32.673 回答