在不调用方法的情况下检测到方法是递归的?
Detect that a method is recursive without calling it?
这听起来像是一件愚蠢的事情。上下文是测试驱动开发:我有一个方法涉及逐步通过树的节点,并开发这个功能我已经走了:
"look at child nodes"
"if child node has children, then look at grandchild nodes"
"if grandchild node has children, then look at great-grandchild nodes"...等等
所以你到了必须用递归方法替换此代码的地步。如果你正在使用 TDD,你想写一个断言语句,如果你的方法不是递归的,它就会失败。这对于非 TDD 的人来说可能听起来很愚蠢,但有一点是树通常涉及相当多的递归功能,所以实际上感觉 "bad" 跳过这种测试步骤!
我想知道 inspect 模块是否有我需要的东西...但我正在努力。在我看来,在理想的世界中,您可能希望检测这种递归性而不必实际调用该方法。
您无法可靠地检查函数是否使用递归,不。
一个简单的递归函数会查找一个具有相同名称的全局函数并调用它,因此您必须查看函数字节码或将字节码解析为 AST 并尝试找到对全局函数的调用具有相同名称的对象。但是,如果使用了 方法 或函数被别名化,那么您将面临更艰巨的检测任务。
此外,您通常测试被测对象的功能,而不是具体实现。测试想要的结果,而不是你如何产生这些结果。
也许您想避免递归,因为您可能 运行 出栈。在那种情况下,你会 测试你是否会 运行 出栈 。
将堆栈深度设置为较小的数字(使用sys.setrecursionlimit()
),创建一个层数比堆栈多的树,然后尝试解析它。如果抛出 RuntimeError
异常,则说明您正在使用递归或其他过度依赖 Python 调用堆栈的方法。
即使在做 'TDD' 时,检查 方法是否是递归的也不是一个好主意。 想想看,即使你能发现那个方法是递归的该 递归代码 可能有上千处错误。
相反,在进行 TDD 时,想出小但很好的表示 data-set 您将要处理的情况,并编写案例以检查您正在编写的函数是否能很好地处理它们。
让我们考虑一个递归函数的例子来解析和找到树节点的总和。
def total(tree):
if tree == None: return 0
return total(tree.left) + total(tree.right) + tree.cargo
以下是一些我会考虑测试 functionality
而不是测试 implementation
函数的测试数据案例:
积极测试以确保总数正确
- 当我在树中有一个节点时,总的是正确的
- 当我有一个节点和两个一级 children 时,总数是否正确
- 当我只有三分之一的右child(左child缺失)是总正确
.
.
然后测试一些退出条件:
- 使用示例树启动测试,并根据您希望实现的算法检查您的函数是否在预期节点上退出
.
.
添加一些负面测试
- 如果树结构中存在循环,则检查抛出的异常
.
.
等等......直到你掌握了一些基本的组合。向上两个 2 层深。
现在您知道要通过这些测试,您的函数必须做 right
事情,递归与否。而那就是单元测试的一个目的。测试 功能 而不是 实现的细节。
一旦您确信您的测试是健壮的,并且通过这些测试的任何函数都是一个好函数,那么如何实现该函数就无关紧要了。它可能是也可能不是递归的,但是您在编写单元测试时不应该关心。
这听起来像是一件愚蠢的事情。上下文是测试驱动开发:我有一个方法涉及逐步通过树的节点,并开发这个功能我已经走了:
"look at child nodes"
"if child node has children, then look at grandchild nodes"
"if grandchild node has children, then look at great-grandchild nodes"...等等
所以你到了必须用递归方法替换此代码的地步。如果你正在使用 TDD,你想写一个断言语句,如果你的方法不是递归的,它就会失败。这对于非 TDD 的人来说可能听起来很愚蠢,但有一点是树通常涉及相当多的递归功能,所以实际上感觉 "bad" 跳过这种测试步骤!
我想知道 inspect 模块是否有我需要的东西...但我正在努力。在我看来,在理想的世界中,您可能希望检测这种递归性而不必实际调用该方法。
您无法可靠地检查函数是否使用递归,不。
一个简单的递归函数会查找一个具有相同名称的全局函数并调用它,因此您必须查看函数字节码或将字节码解析为 AST 并尝试找到对全局函数的调用具有相同名称的对象。但是,如果使用了 方法 或函数被别名化,那么您将面临更艰巨的检测任务。
此外,您通常测试被测对象的功能,而不是具体实现。测试想要的结果,而不是你如何产生这些结果。
也许您想避免递归,因为您可能 运行 出栈。在那种情况下,你会 测试你是否会 运行 出栈 。
将堆栈深度设置为较小的数字(使用sys.setrecursionlimit()
),创建一个层数比堆栈多的树,然后尝试解析它。如果抛出 RuntimeError
异常,则说明您正在使用递归或其他过度依赖 Python 调用堆栈的方法。
即使在做 'TDD' 时,检查 方法是否是递归的也不是一个好主意。 想想看,即使你能发现那个方法是递归的该 递归代码 可能有上千处错误。
相反,在进行 TDD 时,想出小但很好的表示 data-set 您将要处理的情况,并编写案例以检查您正在编写的函数是否能很好地处理它们。
让我们考虑一个递归函数的例子来解析和找到树节点的总和。
def total(tree):
if tree == None: return 0
return total(tree.left) + total(tree.right) + tree.cargo
以下是一些我会考虑测试 functionality
而不是测试 implementation
函数的测试数据案例:
积极测试以确保总数正确
- 当我在树中有一个节点时,总的是正确的
- 当我有一个节点和两个一级 children 时,总数是否正确
- 当我只有三分之一的右child(左child缺失)是总正确 . .
然后测试一些退出条件:
- 使用示例树启动测试,并根据您希望实现的算法检查您的函数是否在预期节点上退出 . .
添加一些负面测试
- 如果树结构中存在循环,则检查抛出的异常 . .
等等......直到你掌握了一些基本的组合。向上两个 2 层深。
现在您知道要通过这些测试,您的函数必须做 right
事情,递归与否。而那就是单元测试的一个目的。测试 功能 而不是 实现的细节。
一旦您确信您的测试是健壮的,并且通过这些测试的任何函数都是一个好函数,那么如何实现该函数就无关紧要了。它可能是也可能不是递归的,但是您在编写单元测试时不应该关心。