【问题标题】:Python unable to compare bound method to itselfPython无法将绑定方法与自身进行比较
【发布时间】:2017-01-28 03:18:25
【问题描述】:

我正在尝试编写一个测试来检查持有类的绑定方法的变量是否与对该方法的另一个引用相同。通常这不是问题,但在同一类的另一个方法中完成时似乎不起作用。这是一个最小的例子:

class TestClass:
    def sample_method(self):
        pass
    def test_method(self, method_reference):
        print(method_reference is self.sample_method)

我真的使用assert 而不是print,但这既不在这里也不在那里,因为最终结果是相同的。测试运行如下:

instance = TestClass()
instance.test_method(instance.sample_method)

结果是False,尽管我预计它是True。该问题在 Python 3.5 和 Python 2.7(在 Anaconda 下运行)都表现出来。

我了解绑定方法是通过执行TestClass.test_method.__get__(instance, type(instance)) 之类的操作获得的闭包。但是,我希望 self.sample_method 已经是对此类闭包的引用,因此 self.sample_methodinstance.sample_method 代表相同的引用。

这里让我感到困惑的部分原因是我正在运行的真实pytest 测试的输出(为matplotlib 工作):

assert <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> is <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>>
E        +  where <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> = <matplotlib.ticker.TransformFormatter object at 0x7f0101077e10>.transform
E        +  and   <bound method TestTransformFormatter.transform1 of <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>> = <matplotlib.tests.test_ticker.TestTransformFormatter object at 0x7f0101077b70>.transform1

如果我正确理解输出,实际比较(第一行)实际上是比较相同的对象,但不知何故出现False。在这一点上我唯一能想到的是 __get__ 实际上被调用了两次,但我不知道为什么/在哪里/如何,也不知道如何解决它。

【问题讨论】:

  • 这完全在意料之中。并非所有instance.sample_methods 都是同一个对象,就像并非所有5024s 都是同一个对象一样。
  • @user2357112。这有点道理。我现在希望澄清的问题是,不同的对象实际上是在哪里创建的?
  • 一次用于instance.sample_method,一次用于self.sample_methodtest_method
  • 这也是有道理的。这是否意味着obj.attr 语法会自动在attr 上调用__get__
  • 如果属性查找找到描述符,那么是的。

标签: python


【解决方案1】:

它们不是同一个引用——代表这两种方法的对象在内存中占据不同的位置:

>>> class TestClass:
...     def sample_method(self):
...         pass
...     def test_method(self, method_reference):
...         print(hex(id(method_reference)))
...         print(hex(id(self.sample_method)))
... 
>>> instance = TestClass()
>>> instance.test_method(instance.sample_method)
0x7fed0cc561c8
0x7fed0cc4e688

不过,更改为 method_reference == self.sample_method 将使断言通过。

编辑因为问题被扩展:似乎是一个有缺陷的测试 - 可能代码的实际功能不需要引用相同(is),只是相等(==)。因此,除了测试之外,您的更改可能没有破坏任何东西。

【讨论】:

  • 感谢您指出== 会起作用。我害怕使用它,因为我知道未绑定的方法实现为 is,但绑定的方法实际上可以检查它们是否有效地相同是有道理的。
  • 现在实际上正在编写测试。我有正确的想法,只是不知道如何在代码中正确表达。
  • 我做了一些更彻底的测试,结果发现 == for methods 是 really weird 并不足以确定两个方法对象是否代表同一个对象的同一个方法。
【解决方案2】:

虽然接受的答案绝不是错误的,但似乎应该注意方法绑定在属性查找上。此外,未绑定方法的行为在 Python 2.X 和 Python 3.X 之间发生了变化。

class A:
    def method(self):
        pass

a = A()
print(a.method is a.method) # False

print(A.method is A.method) # Python 3.X: True, Python 2.X: False

【讨论】:

  • 你能补充一些关于描述符机制的东西吗?
  • 另外,为什么未绑定方法示例在Py2中返回False?它会与== 一起使用吗?
猜你喜欢
  • 2016-07-10
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多