如何在python类中检测重复的方法名称?

Sco*_*len 21 python

在编写单元测试时,我有时剪切并粘贴测试,不记得更改方法名称.这导致覆盖先前的测试,有效地隐藏它并阻止它运行.例如;

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())
Run Code Online (Sandbox Code Playgroud)

在这种情况下,只会调用后一个测试.是否有一种以编程方式捕获此类错误的方法,而不是直接解析原始源代码?

Cas*_*all 24

如果您对代码运行pylint,它会在您覆盖另一个方法时通知您:

例如,我跑了这个:

class A(object):
    def blah(self):
        print("Hello World!")

    def blah(self):
        print("I give up!")
Run Code Online (Sandbox Code Playgroud)

这个在线pylint检查器.除了所有缺少的文档字符串,我得到这个:

E: 5:A.blah: method already defined line 2 
Run Code Online (Sandbox Code Playgroud)

  • 或者,如果你想要一个不那么烦人的linter,那就是pyflakes:https://launchpad.net/pyflakes (3认同)

kin*_*all 14

接下来是使用未记录的,特定于实现的Python功能的可怕黑客攻击.你永远不应该做这样的事情.

它已经在Python 2.6.1和2.7.2上进行了测试; 似乎没有使用Python 3.2编写,但是,无论如何,你可以在Python 3.x中做到这一点.

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)
Run Code Online (Sandbox Code Playgroud)

用法:

with NoDupNames():
    class Foo(object):
        num = None
        num = 42
Run Code Online (Sandbox Code Playgroud)

结果:

NameError: attribute 'num' already declared
Run Code Online (Sandbox Code Playgroud)

工作原理:我们连接到系统跟踪钩子.每次Python即将执行一行时,我们都会被调用.这允许我们查看最后执行的语句定义了哪些名称.为了确保我们能够捕获重复项,我们实际上维护了自己的局部变量字典并在每行之后清除了 Python.在类定义的最后,我们将本地复制回Python.其他一些tomfoolery用于处理嵌套类定义并在单个语句中处理多个赋值.

作为一个缺点,我们"清除所有当地人!" 方法意味着你不能这样做:

with NoDupNames():
    class Foo(object):
        a = 6
        b = 7
        c = a * b
Run Code Online (Sandbox Code Playgroud)

因为据Python所知,没有名字a,b何时c = a * b执行; 我们一见到它们就清理掉了.此外,如果您在一行中分配两次相同的变量(例如a = 0; a = 1),则不会捕获该变量.但是,它适用于更典型的类定义.

此外,您不应该在NoDupNames上下文中放置除类定义之外的任何内容.我不知道会发生什么; 也许没什么不好.但是我没有尝试过,所以理论上宇宙可以被吸进自己的插孔中.

这很可能是我写过的最邪恶的代码,但它确实很有趣!


And*_*ark 6

以下是如何使用装饰器在运行时检测此问题的一个选项,而无需任何分析工具:

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())
Run Code Online (Sandbox Code Playgroud)

尝试导入或运行的示例:

>>> 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
Run Code Online (Sandbox Code Playgroud)