Dynamic Semantic errors in Python

独自空忆成欢 提交于 2019-12-03 22:22:29

Can we control the output that is given out by Python compiler, like print some other error message, skip that division by zero operation, and carry on with rest of the instructions?

No, you cannot. You can manually wrap every dangerous command with a try...except block, but I'm assuming you're talking about an automatic recovery to specific lines within a try...except block, or even completely automatically.

By the time the error has fallen through such that sys.excepthook is called, or whatever outer scope if you catch it early, the inner scopes are gone. You can change line numbers with sys.settrace in CPython although that is only an implementation detail, but since the outer scopes are gone there is no reliable recorvery mechanism.

If you try to use the humorous goto April fools module (that uses the method I just described) to jump blocks even within a file:

from goto import goto, label

try:
    1 / 0
    label .foo
    print("recovered")

except:
    goto .foo

you get an error:

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

so I'm pretty certain it's impossible.


And also, how can i evaluate the cost of run-time semantic checks?

I don't know what that is, but you're probably looking for a 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()

output:

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

So the "run-time semantic check", in this case would be taking 36.4% of the time of running foo.


If you want to time specific blocks manually that are larger than you'd use timeit on but smaller than you'd want for a profiler, instead of using two time.time() calls (which is quite an inaccurate method) I suggest Steven D'Aprano's Stopwatch context manager.

I would just use an exception, this example is using python 3. For Python 2, simple remove the annotations after the function parameters. So you function signature would look like this -> 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

This is an example of how you could catch Zero Division, by making an exception case using ZeroDivisionError.

I won't go into any specific tools for making loggers, but you can indeed understand the costs associated with this kind of checking. You can put a start = time.time() at the start of the function and end = time.time() at the end. If you take the difference, you will get the execution time in seconds.

I hope that helps.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!