Python程序的跟踪表

2024-06-07 05:27:58 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有办法得到Python程序的跟踪表?或者让一个程序运行另一个程序并得到它的跟踪表?我是一名教师,试图完美地验证我们在测试中使用的跟踪问题的答案。

因此,例如,假设我有一个名为problem1.py的Python程序,其内容如下:

问题1.py

 a = 1
 b = 2

 a = a + b

执行假定的程序traceTable.py应该如下:

 $ python traceTable.py problem1.py
 L || a | b
 1 || 1 |
 2 || 1 | 2
 4 || 3 | 2

(或使用不同语法的相同信息)

我已经研究了trace模块,但我看不出它支持这个功能的方式。


更新

女士们先生们:利用内德·巴切尔德的绝妙建议,我给你们traceTable.py

嗯。。几乎。正如您在Ned Batchelder的例子中所看到的,frame.f_lineno并不总是表现得很直观(例如,两行3&4都算作第4行),但是行号足够接近,可以进行相当好的引用。而且,所有的计算都是正确的。

我用一个包含if语句的长程序对此进行了测试,它给出了正确的表(没有行号)。

你也会注意到我的程序比Ned Batchelder的概念证明要长得多,因为他提到的大型程序中包含了“更有趣的数据生态系统”。在使用execfile以及管理它和减少噪声(alaignored_variables)以及生成正确字符串输出所需的所有变量的范围内,需要更多的代码:

可追踪.py

 '''
 Usage: python traceTable.py program

     -program  Python program to be traced
 '''

 import sys

 if len(sys.argv) < 2:
      print __doc__
      exit()
 else:
      file_name = sys.argv[1]

 past_locals = {}
 variable_list = []
 table_content = ""

 ignored_variables = set([
      'file_name',
      'trace',
      'sys',
      'past_locals',
      'variable_list',
      'table_content',
      'getattr',
      'name',
      'self',
      'object',
      'consumed',
      'data',
      'ignored_variables'])

 def trace(frame, event, arg_unused):
      global past_locals, variable_list, table_content, ignored_variables
      relevant_locals = {}
      all_locals = frame.f_locals.copy()
      for k,v in all_locals.items():
           if not k.startswith("__") and k not in ignored_variables:
                relevant_locals[k] = v
      if len(relevant_locals) > 0 and past_locals != relevant_locals:
           for i in relevant_locals:
                if i not in past_locals:
                     variable_list.append(i)
           table_content += str(frame.f_lineno) + " || "
           for variable in variable_list:
                table_content += str(relevant_locals[variable]) + " | "
           table_content = table_content[:-2]
           table_content += '\n'
           past_locals = relevant_locals
      return trace

 sys.settrace(trace)

 execfile(file_name)

 table_header = "L || "
 for variable in variable_list:
      table_header += variable + ' | '
 table_header = table_header[:-2]
 print table_header
 print table_content

当被调用时,它会产生输出

 $ python traceTable.py problem1.py
 L || a | b
 2 || 1
 4 || 1 | 2
 4 || 3 | 2

Tags: inpy程序ifsystabletracecontent
3条回答

这不是当前Python跟踪工具支持的用例,但是应该可以构建。我不知道你如何决定输出哪些列。在您的示例中,a和b是唯一的局部变量,但是较大的程序将具有更有趣的数据生态系统。

更新:这里有一个简单的概念证明:

 1     import sys
 2
 3     def trace(frame, event, arg_unused):
 4         print event, frame.f_lineno, frame.f_locals
 5         return trace
 6
 7     sys.settrace(trace)
 8
 9     def foo():
10         a = 1
11         b = 2
12
13         a = a + b
14
15     foo()

运行时,输出为:

call 9 {}
line 10 {}
line 11 {'a': 1}
line 13 {'a': 1, 'b': 2}
return 13 {'a': 3, 'b': 2}

根据ned batchelder的建议,作为一名教师,我制作了一个Tracer类,帮助创建LaTeX输出longtable显示带有选择变量的程序跟踪,绕过input()以实现过程自动化(特别是当由powerfull bashful LaTeX包中的\bash宏调用时)。

追踪.py:

import sys
class Tracer():
    def __init__(self, varList=[], startLine=1, jeuEssai=[]):
        """
        Arguments :
        \tvarList\ttraced variable list (used as column header)
        \tstartLine\toffset numbering line from the beginning of the program
        \tjeuEssai\tinput values to be sent to the automated input bypass
        """
        self.traced_variables = varList
        self.traced_line_start = startLine
        self.input_values = jeuEssai
        self.input_cursor = int(0)
        self.traced_variables_new_values = dict( (k, '') for k in self.traced_variables)

        print("\\begin{longtable}{c*{%i}{>{\\ttfamily}c}}" % len(self.traced_variables), file=sys.stderr, flush=True)
        print("\t\\hline\\no ligne",end='', file=sys.stderr)
        for header in self.traced_variables:
            print(" &", header,end='', file=sys.stderr)
        print(" \\\\ \\hline", file=sys.stderr)
        sys.settrace(self.tracer_programme_latex)


    def tracer_programme_latex(self, frame, event, args):
        if frame.f_code.co_name not in ['input','print','close']:
            if event == "line":
                output = str()
                for var in self.traced_variables:
                    current_val = str(frame.f_locals.get(var, "-"))
                    if str(self.traced_variables_new_values.get(var, "-")) != current_val:
                        self.traced_variables_new_values[var] = current_val
                        current_val = "\hit{}" + current_val
                    output += " & "
                    output += current_val
                output += " \\\\"
                print("\t%s%s" % (str(frame.f_lineno - self.traced_line_start), output), file=sys.stderr, flush=True)
        return self.tracer_programme_latex


    def close(self):
        """Close the 'longtable' LaTeX environnement."""
        print("\\end{longtable}", file=sys.stderr, flush=True)


    def input(self, prompt=None):
        """
        bypass de la fonction 'input()' pour injecter
        les valeurs d'essais.
        Le jeu d'essai est fourni de manière cyclique. Cela peut
        causer des boucles infinies si vous ne fournissez pas une
        valeur permettant de réaliser l'arrêt des entrées (dans le
        cas bien-sûr où 'input()' est appelé dans une boucle).
        """
        self.input_cursor = (1 + self.input_cursor) % len(self.input_values)
        return self.input_values[self.input_cursor - 1]


    def print(self, *args):
        pass

接下来,您可以找到一个示例,并生成输出:

程序.py:

def factor():
    question = "Give a number: "
    number = float(input(question))
    product = 1
    while number != 0 :
        product *= number
        print("Product:", product)
        number = float(input(question))

if __name__ == "__main__":
    import sys
    TRACING = len(sys.argv) == 2 and sys.argv[1] == 'trace'
    if TRACING:
        from tracer import Tracer
        t = Tracer(varList=['question','number','product'], startLine=2, jeuEssai=[7,6,5,-8,0])
        input = t.input

    factor()
    if TRACING:
        t.close()

标准输出:(由python3 program.py调用时)

Give a number: 7
Product: 7.0
Give a number: 6
Product: 42.0
Give a number: 5
Product: 210.0
Give a number: -8
Product: -1680.0
Give a number: 0

跟踪输出:(由python3 program.py trace 1>/dev/null调用时)

\begin{longtable}{c*{3}{>{\ttfamily}c}}
    \hline\no ligne & question & number & product \\ \hline
    0 & \hit{}- & \hit{}- & \hit{}- \\
    1 & \hit{}Give a number:  & - & - \\
    2 & Give a number:  & \hit{}7.0 & - \\
    3 & Give a number:  & 7.0 & \hit{}1 \\
    4 & Give a number:  & 7.0 & 1 \\
    5 & Give a number:  & 7.0 & \hit{}7.0 \\
    6 & Give a number:  & 7.0 & 7.0 \\
    3 & Give a number:  & \hit{}6.0 & 7.0 \\
    4 & Give a number:  & 6.0 & 7.0 \\
    5 & Give a number:  & 6.0 & \hit{}42.0 \\
    6 & Give a number:  & 6.0 & 42.0 \\
    3 & Give a number:  & \hit{}5.0 & 42.0 \\
    4 & Give a number:  & 5.0 & 42.0 \\
    5 & Give a number:  & 5.0 & \hit{}210.0 \\
    6 & Give a number:  & 5.0 & 210.0 \\
    3 & Give a number:  & \hit{}-8.0 & 210.0 \\
    4 & Give a number:  & -8.0 & 210.0 \\
    5 & Give a number:  & -8.0 & \hit{}-1680.0 \\
    6 & Give a number:  & -8.0 & -1680.0 \\
    3 & Give a number:  & \hit{}0.0 & -1680.0 \\
\end{longtable}

值更改时插入\hit{}宏。由您定义一些相关的内容,如着色宏:\newcommand{\hit}{\color{red}}

您可以使用Python debugger,虽然我不知道如何让它自己单步执行,但它很可能可以,然后您可以解析输出。

下面是一个非常粗糙的例子:

添加.py

a = 1
b = 2

a = a + b

运行它。。。

PS >python -m pdb adding.py
> adding.py(1)<module>()
-> a = 1
(Pdb) alias stepprint step;;print a;;print b
(Pdb) stepprint
> adding.py(2)<module>()
-> b = 2
1
*** NameError: name 'b' is not defined
(Pdb) stepprint
> adding.py(4)<module>()
-> a = a + b
1
2
(Pdb) stepprint
--Return--
> adding.py(4)<module>()->None
-> a = a + b
3
2
(Pdb) stepprint
--Return--
> <string>(1)<module>()->None
3
2
(Pdb) stepprint
The program finished and will be restarted
> adding.py(1)<module>()
-> a = 1
*** NameError: name 'a' is not defined
*** NameError: name 'b' is not defined
(Pdb) q

PS >

结束(q)在“程序完成”位。

相关问题 更多 >