0

我经常发现自己试图稍微滥用 python 允许的一些动态性(我在这里使用 python3,但应该没有太多差异)。

在这种情况下,我想将我的unittest.TestCase, 中的单个 test_ 方法拆分为在运行时创建的多个方法。

(这是一个关于罗马数字的Kata,但我实际上没有TDD:我后来写了测试)

这是测试:

class TestNumbers(TestCase):
    def test_number(self):
            for i in range(3000):
                    self.assertEqual(i, roman_to_int(int_to_roman(i)))

这就是我尝试编写它的方式:

from functools import partial
from types import MethodType

class TestNumbers(TestCase):
    pass

def test_number(self, n):
    self.assertEqual(n, roman_to_int(int_to_roman(n)))

for i in range(3000):
    name = str(i)
    test_method = MethodType(partial(test_number, n=i), TestNumbers)
    setattr(TestNumbers, "test" + name, test_method)

(或者,我也尝试动态地创建许多 TestCase 子类和setattr(globals(), ...)它们)

我知道:这并没有太多目的,它也可能更慢,等等。但这只是一个 POC,我正在尝试了解如何让它工作

通过使用 MethodType,测试变成了一个绑定方法,但是在里面,assertEqual 显然变成了一个函数,当试图调用它时它会失败TypeError: assertEqual() takes at least 3 arguments (2 given)

我试图将 test_number 更改为

def test_number(self, n):
    self.assertEqual(self, n, roman_to_int(int_to_roman(n)))

但这只会在隐藏的 TestCase 方法中更深地挖掘出同样的问题:TypeError: _getAssertEqualityFunc() takes exactly 3 arguments (2 given)

我在stackoverflow上查看了here并发现了类似的问题(如Python:绑定一个未绑定的方法?),但没有一个涉及绑定一个在其中调用目标类的其他绑定方法的方法

我还尝试研究元类(http://docs.python.org/py3k/reference/datamodel.html#customizing-class-creation),但它似乎与我想要做的不匹配

4

2 回答 2

1

如果您将方法直接添加到类中,则无需自己绑定它。

class C(object):
  def __init__(self):
    self.foo = 42

def getfoo(self):
  return self.foo

C.getfoo = getfoo
c=C()
print(c.getfoo())
于 2012-04-19T17:39:24.590 回答
1

在 Python 2 上,有函数、未绑定和绑定方法。将方法作为实例绑定到类不会使其成为未绑定方法,而是使其等效于类方法或元类方法。

在 Python 3 上,不再有绑定和未绑定的方法,只有函数和方法,所以如果你将 assertEqual 作为一个函数,这意味着你的 testx 方法没有绑定到实例,这才是真正的问题。

在 Python 2 上,您所要做的就是在 MethodType 调用上将 None 分配给实例,它就会起作用。

所以,替换:

test_method = MethodType(partial(test_number, n=i), TestNumbers)

为了:

test_method = MethodType(partial(test_number, n=i), None, TestNumbers)

在 Python 3 上,只需将函数分配给类就可以了,就像其他答案所暗示的那样,但在您的情况下,真正的问题是部分对象不会成为方法。

对于您的情况,一个简单的解决方案是使用 lambda 而不是 partial。

代替:

test_method = MethodType(partial(test_number, n=i), TestNumbers)

利用:

test_method = lambda self: test_number(self, i)

它应该工作......

真正巧妙的解决方案是重写 partial 以返回具有所需参数的真实函数。您可以使用旧版本中的所有内容和额外的默认参数创建一个函数实例。像这样的东西:

code = test_number.__code__
globals = test_number.__globals__
closure = test_number.__closure__

testx = FunctionType(code, globals, "test"+name, (i,), closure)
setattr(TestNumbers, "test" + name, testx)
于 2012-04-19T18:02:23.030 回答