How can I detect duplicate method names in a python class?
在编写单元测试时,我有时剪切粘贴一个测试,不记得更改方法名。这会导致覆盖上一个测试,有效地隐藏它并阻止它运行。例如;
1 2 3 4 5 6 7 8 | class WidgetTestCase(unittest.TestCase): def test_foo_should_do_some_behavior(self): self.assertEquals(42, self.widget.foo()) def test_foo_should_do_some_behavior(self): self.widget.bar() self.assertEquals(314, self.widget.foo()) |
在这种情况下,只调用后一个测试。除了直接解析原始源代码之外,是否有一种以编程方式捕获此类错误的方法?
如果您在代码上运行pylint,它将在您覆盖另一个方法时通知您:
例如,我运行了以下命令:
1 2 3 4 5 6 | class A(object): def blah(self): print("Hello World!") def blah(self): print("I give up!") |
在这个在线皮林检查。除了所有丢失的文档字符串等,我还得到了:
1 | E: 5:A.blah: method already defined line 2 |
接下来是一个可怕的黑客,它使用了未记录的、特定于实现的Python特性。你不应该做这种事。
它已经在python 2.6.1和2.7.2上进行了测试;看起来不像上面写的那样可以与python 3.2一起使用,但是无论如何,您都可以在python3.x中正确地进行测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import sys class NoDupNames(object): def __init__(self): self.namespaces = [] def __call__(self, frame, event, arg): if event =="call": if frame.f_code.co_flags == 66: self.namespaces.append({}) elif event in ("line","return") and self.namespaces: for key in frame.f_locals.iterkeys(): if key in self.namespaces[-1]: raise NameError("attribute '%s' already declared" % key) self.namespaces[-1].update(frame.f_locals) frame.f_locals.clear() if event =="return": frame.f_locals.update(self.namespaces.pop()) return self def __enter__(self): self.oldtrace = sys.gettrace() sys.settrace(self) def __exit__(self, type, value, traceback): sys.settrace(self.oldtrace) |
用途:
1 2 3 4 | with NoDupNames(): class Foo(object): num = None num = 42 |
结果:
1 | NameError: attribute 'num' already declared |
工作原理:我们连接到系统跟踪挂钩。每次Python要执行一行时,我们都会被调用。这允许我们查看最后执行的语句所定义的名称。为了确保能够捕获重复的内容,我们实际上维护了自己的局部变量字典,并在每行之后清除了python。在类定义的末尾,我们将局部变量复制回python中。其中的一些tombourry用于处理嵌套类定义,并在一条语句中处理多个赋值。
不利的是,我们的"清除所有当地人!"方法意味着你不能这样做:
1 2 3 4 5 | with NoDupNames(): class Foo(object): a = 6 b = 7 c = a * b |
因为据python所知,在执行
此外,除了类定义之外,您不应该将任何内容放在
这很可能是我写过的最邪恶的代码,但它确实很有趣!
下面是一个选项,用于在运行时使用decorator检测此问题,而不需要任何分析工具:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def one_def_only(): names = set() def assert_first_def(func): assert func.__name__ not in names, func.__name__ + ' defined twice' names.add(func.__name__) return func return assert_first_def class WidgetTestCase(unittest.TestCase): assert_first_def = one_def_only() @assert_first_def def test_foo_should_do_some_behavior(self): self.assertEquals(42, self.widget.foo()) @assert_first_def def test_foo_should_do_some_behavior(self): self.widget.bar() self.assertEquals(314, self.widget.foo()) |
尝试导入或运行的示例:
1 2 3 4 5 6 7 8 9 10 | >>> import testcases Traceback (most recent call last): File"<stdin>", line 1, in <module> File"testcases.py", line 13, in <module> class WidgetTestCase(unittest.TestCase): File"testcases.py", line 20, in WidgetTestCase @assert_first_def File"testcases.py", line 7, in assert_first_def assert func.__name__ not in names, func.__name__ + ' defined twice' AssertionError: test_foo_should_do_some_behavior defined twice |
您不能在运行时轻松/干净地检测它,因为旧方法被简单地替换,并且必须在每个函数定义上使用修饰符。静态分析(pylint等)是最好的方法。
但是,如果方法被覆盖,则可以创建实现-just tested it and