【问题标题】:Bind a method that calls other methods inside the class绑定一个调用类内其他方法的方法
【发布时间】:2012-05-01 06:58:48
【问题描述】:

我经常发现自己试图稍微滥用 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 上查看了这里并发现了类似的问题(例如 Python: Bind an Unbound Method? ),但没有一个涉及绑定一个在其中调用目标类的其他绑定方法的方法

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

【问题讨论】:

    标签: python dynamic methods bind


    【解决方案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)
    

    【讨论】:

    • assertEqual does 显然成为一个函数:paste.pocoo.org/show/584485 而在 python3 MethodType 中只有 2 个参数:paste.pocoo.org/show/584486
    • 在 Python 3 上没有未绑定和绑定的方法,只有方法。我相应地更改了答案。
    • 部分对象不会成为方法我完全没有想到!谢谢:D
    【解决方案2】:

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

    class C(object):
      def __init__(self):
        self.foo = 42
    
    def getfoo(self):
      return self.foo
    
    C.getfoo = getfoo
    c=C()
    print(c.getfoo())
    

    【讨论】:

    • mhn,是的......即使我从子类调用它并动态设置它的名称,它也可以工作 paste.pocoo.org/show/584190 但是当尝试在 unittest.TestCase 上执行它时它会失败。 ..也许是由于测试运行程序实例化类的方式?我稍后会调查并最终更新问题
    猜你喜欢
    • 2017-06-25
    • 2017-02-27
    • 1970-01-01
    • 1970-01-01
    • 2017-06-20
    • 2021-12-07
    • 1970-01-01
    相关资源
    最近更新 更多